summaryrefslogtreecommitdiff
path: root/userland/libc
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-22 19:50:38 +0200
committerAnton Kling <anton@kling.gg>2023-10-22 19:50:38 +0200
commit4e09bca9e34c226b6d7e34b4fa11248405fd988e (patch)
tree80f156b7940d9d19971395f335530170c69516c7 /userland/libc
Move everything into a new repo.
Diffstat (limited to 'userland/libc')
-rw-r--r--userland/libc/Makefile26
-rw-r--r--userland/libc/arpa/inet.h4
-rw-r--r--userland/libc/arpa/inet/htonl.c11
-rw-r--r--userland/libc/arpa/inet/htons.c10
-rw-r--r--userland/libc/assert.c9
-rw-r--r--userland/libc/assert.h10
-rw-r--r--userland/libc/crt0.s13
-rw-r--r--userland/libc/ctype.h12
-rw-r--r--userland/libc/ctype/isalnum.c6
-rw-r--r--userland/libc/ctype/isalpha.c4
-rw-r--r--userland/libc/ctype/isascii.c4
-rw-r--r--userland/libc/ctype/isdigit.c6
-rw-r--r--userland/libc/ctype/isprint.c3
-rw-r--r--userland/libc/ctype/ispunct.c6
-rw-r--r--userland/libc/ctype/isxdigit.c6
-rw-r--r--userland/libc/ctype/tolower.c7
-rw-r--r--userland/libc/ctype/toupper.c7
-rw-r--r--userland/libc/dirent.h28
-rw-r--r--userland/libc/dirent/alphasort.c7
-rw-r--r--userland/libc/dirent/closedir.c7
-rw-r--r--userland/libc/dirent/opendir.c11
-rw-r--r--userland/libc/dirent/readdir.c14
-rw-r--r--userland/libc/dirent/scandir.c43
-rw-r--r--userland/libc/endian.h2
-rw-r--r--userland/libc/errno.h87
-rw-r--r--userland/libc/fcntl.h10
-rw-r--r--userland/libc/include/arpa/inet.h0
-rw-r--r--userland/libc/include/assert.h10
-rw-r--r--userland/libc/include/byteswap.h0
-rw-r--r--userland/libc/include/ctype.h14
-rw-r--r--userland/libc/include/dirent.h28
-rw-r--r--userland/libc/include/endian.h2
-rw-r--r--userland/libc/include/err.h0
-rw-r--r--userland/libc/include/errno.h87
-rw-r--r--userland/libc/include/fcntl.h12
-rw-r--r--userland/libc/include/fnmatch.h0
-rw-r--r--userland/libc/include/glob.h10
-rw-r--r--userland/libc/include/grp.h0
-rw-r--r--userland/libc/include/input.h9
-rw-r--r--userland/libc/include/inttypes.h19
-rw-r--r--userland/libc/include/langinfo.h0
-rw-r--r--userland/libc/include/libgen.h6
-rw-r--r--userland/libc/include/limits.h4
-rw-r--r--userland/libc/include/locale.h0
-rw-r--r--userland/libc/include/math.h0
-rw-r--r--userland/libc/include/net/if.h0
-rw-r--r--userland/libc/include/netdb.h0
-rw-r--r--userland/libc/include/netinet/in.h0
-rw-r--r--userland/libc/include/netinet/tcp.h0
-rw-r--r--userland/libc/include/paths.h0
-rw-r--r--userland/libc/include/poll.h17
-rw-r--r--userland/libc/include/pty.h6
-rw-r--r--userland/libc/include/pwd.h0
-rw-r--r--userland/libc/include/regex.h0
-rw-r--r--userland/libc/include/sched.h0
-rw-r--r--userland/libc/include/setjmp.h14
-rw-r--r--userland/libc/include/signal.h9
-rw-r--r--userland/libc/include/socket.h41
-rw-r--r--userland/libc/include/stdio.h116
-rw-r--r--userland/libc/include/stdlib.h32
-rw-r--r--userland/libc/include/string.h29
-rw-r--r--userland/libc/include/strings.h0
-rw-r--r--userland/libc/include/sys/ioctl.h10
-rw-r--r--userland/libc/include/sys/mman.h15
-rw-r--r--userland/libc/include/sys/mount.h0
-rw-r--r--userland/libc/include/sys/resource.h0
-rw-r--r--userland/libc/include/sys/socket.h0
-rw-r--r--userland/libc/include/sys/stat.h31
-rw-r--r--userland/libc/include/sys/statvfs.h0
-rw-r--r--userland/libc/include/sys/syscall.h0
-rw-r--r--userland/libc/include/sys/time.h41
-rw-r--r--userland/libc/include/sys/times.h0
-rw-r--r--userland/libc/include/sys/types.h28
-rw-r--r--userland/libc/include/sys/ucontext.h0
-rw-r--r--userland/libc/include/sys/un.h0
-rw-r--r--userland/libc/include/sys/utsname.h0
-rw-r--r--userland/libc/include/sys/wait.h0
-rw-r--r--userland/libc/include/syscall.h150
-rw-r--r--userland/libc/include/syslog.h0
-rw-r--r--userland/libc/include/termios.h0
-rw-r--r--userland/libc/include/time.h35
-rw-r--r--userland/libc/include/ubsan.h79
-rw-r--r--userland/libc/include/unistd.h24
-rw-r--r--userland/libc/include/utime.h0
-rw-r--r--userland/libc/include/wchar.h0
-rw-r--r--userland/libc/include/wctype.h0
-rw-r--r--userland/libc/input.h9
-rw-r--r--userland/libc/inttypes.h5
-rw-r--r--userland/libc/isspace.c5
-rw-r--r--userland/libc/libc.c287
-rw-r--r--userland/libc/libgen/basename.c45
-rw-r--r--userland/libc/libgen/dirname.c44
-rw-r--r--userland/libc/limits.h2
-rw-r--r--userland/libc/malloc/malloc.c232
-rw-r--r--userland/libc/malloc/malloc.h9
-rw-r--r--userland/libc/malloc/oldmalloc.c136
-rw-r--r--userland/libc/math.h0
-rw-r--r--userland/libc/memset.c15
-rw-r--r--userland/libc/mmap.c19
-rw-r--r--userland/libc/poll.h16
-rw-r--r--userland/libc/pty.c15
-rw-r--r--userland/libc/pty.h6
-rw-r--r--userland/libc/setjmp/longjmp.s16
-rw-r--r--userland/libc/setjmp/setjmp.s23
-rw-r--r--userland/libc/socket.h41
-rw-r--r--userland/libc/stdio.h95
-rw-r--r--userland/libc/stdio/dprintf.c9
-rw-r--r--userland/libc/stdio/fclose.c10
-rw-r--r--userland/libc/stdio/feof.c5
-rw-r--r--userland/libc/stdio/ferror.c5
-rw-r--r--userland/libc/stdio/fflush.c7
-rw-r--r--userland/libc/stdio/fgetc.c20
-rw-r--r--userland/libc/stdio/fgetpos.c7
-rw-r--r--userland/libc/stdio/fgets.c16
-rw-r--r--userland/libc/stdio/fileno.c13
-rw-r--r--userland/libc/stdio/fopen.c57
-rw-r--r--userland/libc/stdio/fprintf.c9
-rw-r--r--userland/libc/stdio/fputc.c7
-rw-r--r--userland/libc/stdio/fputs.c9
-rw-r--r--userland/libc/stdio/fread.c11
-rw-r--r--userland/libc/stdio/fscanf.c7
-rw-r--r--userland/libc/stdio/fseek.c21
-rw-r--r--userland/libc/stdio/fsetpos.c7
-rw-r--r--userland/libc/stdio/ftell.c5
-rw-r--r--userland/libc/stdio/fwrite.c12
-rw-r--r--userland/libc/stdio/getchar.c4
-rw-r--r--userland/libc/stdio/open_memstream.c108
-rw-r--r--userland/libc/stdio/printf.c9
-rw-r--r--userland/libc/stdio/putc.c3
-rw-r--r--userland/libc/stdio/putchar.c7
-rw-r--r--userland/libc/stdio/puts.c6
-rw-r--r--userland/libc/stdio/remove.c9
-rw-r--r--userland/libc/stdio/rename.c8
-rw-r--r--userland/libc/stdio/setvbuf.c6
-rw-r--r--userland/libc/stdio/snprintf.c42
-rw-r--r--userland/libc/stdio/sprintf.c33
-rw-r--r--userland/libc/stdio/stderr.c11
-rw-r--r--userland/libc/stdio/stdin.c54
-rw-r--r--userland/libc/stdio/stdout.c13
-rw-r--r--userland/libc/stdio/tmpfile.c9
-rw-r--r--userland/libc/stdio/tmpnam.c10
-rw-r--r--userland/libc/stdio/ungetc.c9
-rw-r--r--userland/libc/stdio/vdprintf.c77
-rw-r--r--userland/libc/stdio/vfprintf.c243
-rw-r--r--userland/libc/stdio/vprintf.c3
-rw-r--r--userland/libc/stdlib.h17
-rw-r--r--userland/libc/stdlib/abort.c10
-rw-r--r--userland/libc/stdlib/abs.c3
-rw-r--r--userland/libc/stdlib/atexit.c6
-rw-r--r--userland/libc/stdlib/atof.c5
-rw-r--r--userland/libc/stdlib/atoi.c4
-rw-r--r--userland/libc/stdlib/getenv.c6
-rw-r--r--userland/libc/stdlib/mkstemp.c14
-rw-r--r--userland/libc/stdlib/qsort.c29
-rw-r--r--userland/libc/stdlib/rand.c17
-rw-r--r--userland/libc/stdlib/srand.c8
-rw-r--r--userland/libc/stdlib/strtod.c70
-rw-r--r--userland/libc/stdlib/strtol.c52
-rw-r--r--userland/libc/stdlib/strtold.c9
-rw-r--r--userland/libc/stdlib/strtoul.c72
-rw-r--r--userland/libc/stdlib/system.c17
-rw-r--r--userland/libc/string.h17
-rw-r--r--userland/libc/string/memcmp.c11
-rw-r--r--userland/libc/string/memcpy.c9
-rw-r--r--userland/libc/string/memmove.c14
-rw-r--r--userland/libc/string/sscanf.c193
-rw-r--r--userland/libc/string/strcasecmp.c28
-rw-r--r--userland/libc/string/strcat.c13
-rw-r--r--userland/libc/string/strchr.c11
-rw-r--r--userland/libc/string/strcmp.c29
-rw-r--r--userland/libc/string/strcpy.c7
-rw-r--r--userland/libc/string/strcspn.c14
-rw-r--r--userland/libc/string/strdup.c15
-rw-r--r--userland/libc/string/strlcpy.c20
-rw-r--r--userland/libc/string/strlen.c8
-rw-r--r--userland/libc/string/strncasecmp.c29
-rw-r--r--userland/libc/string/strncmp.c28
-rw-r--r--userland/libc/string/strncpy.c13
-rw-r--r--userland/libc/string/strndup.c22
-rw-r--r--userland/libc/string/strnlen.c9
-rw-r--r--userland/libc/string/strpbrk.c12
-rw-r--r--userland/libc/string/strrchr.c12
-rw-r--r--userland/libc/string/strspn.c18
-rw-r--r--userland/libc/string/strstr.c21
-rw-r--r--userland/libc/string/strtok.c26
-rw-r--r--userland/libc/strings.h0
-rw-r--r--userland/libc/sys/mman.h8
-rw-r--r--userland/libc/sys/mman/mmap.c19
-rw-r--r--userland/libc/sys/stat.h30
-rw-r--r--userland/libc/sys/stat/mkdir.c9
-rw-r--r--userland/libc/sys/stat/stat.c13
-rw-r--r--userland/libc/sys/time/gettimeofday.c5
-rw-r--r--userland/libc/sys/types.h27
-rw-r--r--userland/libc/syscall.h149
-rw-r--r--userland/libc/time.h20
-rw-r--r--userland/libc/time/clock_gettime.c14
-rw-r--r--userland/libc/time/ctime_r.c13
-rw-r--r--userland/libc/time/gmtime.c21
-rw-r--r--userland/libc/time/localtime.c21
-rw-r--r--userland/libc/time/strftime.c7
-rw-r--r--userland/libc/time/time.c9
-rw-r--r--userland/libc/ubsan.c49
-rw-r--r--userland/libc/ubsan.h79
-rw-r--r--userland/libc/unistd.h14
-rw-r--r--userland/libc/unistd/_exit.c8
-rw-r--r--userland/libc/unistd/execvp.c8
-rw-r--r--userland/libc/unistd/getopt.c14
-rw-r--r--userland/libc/unistd/getpid.c7
-rw-r--r--userland/libc/unistd/msleep.c6
-rw-r--r--userland/libc/unistd/unlink.c7
-rw-r--r--userland/libc/unistd/uptime.c6
-rw-r--r--userland/libc/wchar.h0
-rw-r--r--userland/libc/wctype.h0
213 files changed, 4633 insertions, 0 deletions
diff --git a/userland/libc/Makefile b/userland/libc/Makefile
new file mode 100644
index 0000000..98d81d4
--- /dev/null
+++ b/userland/libc/Makefile
@@ -0,0 +1,26 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+AR="/home/anton/prj/osdev/sysroot/bin/i686-sb-ar"
+AS="/home/anton/prj/osdev/sysroot/bin/i686-sb-as"
+#CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -I./include/ -static -fsanitize=shift,signed-integer-overflow,bounds
+CFLAGS = -ggdb -ffreestanding -O2 -Wall -pedantic -Wimplicit-fallthrough -I./include/ -static
+#BINS=c.a libc.o crt0.o malloc.o pty.o mmap.o
+OBJ=crt0.o libc.o malloc/malloc.o pty.o sys/mman/mmap.o memset.o assert.o stdio/snprintf.o stdio/vfprintf.o string/memcpy.o string/memcmp.o string/strcmp.o ubsan.o string/strcpy.o isspace.o stdio/puts.o stdio/putchar.o dirent/opendir.o dirent/readdir.o dirent/closedir.o unistd/getopt.o dirent/scandir.o dirent/alphasort.o stdio/printf.o stdio/vdprintf.o stdio/vprintf.o stdio/dprintf.o stdio/vprintf.o string/strlen.o string/strnlen.o stdio/stdin.o stdio/stdout.o stdio/stderr.o stdio/getchar.o stdio/fgetc.o arpa/inet/htons.o arpa/inet/htonl.o stdio/fread.o stdio/fwrite.o stdio/fopen.o stdio/fclose.o stdio/fseek.o ctype/isascii.o stdio/fprintf.o stdlib/atoi.o stdlib/strtol.o ctype/toupper.o ctype/tolower.o string/strcat.o string/strchr.o string/sscanf.o sys/stat/stat.o stdlib/getenv.o string/strrchr.o stdio/ftell.o stdio/tmpfile.o stdio/fgets.o stdio/feof.o stdio/fscanf.o stdio/ungetc.o string/strncmp.o stdio/fputc.o string/strncpy.o stdio/remove.o stdio/ferror.o stdio/fputs.o stdlib/rand.o stdlib/srand.o unistd/getpid.o stdlib/strtoul.o stdio/fflush.o stdlib/abort.o string/strcspn.o time/localtime.o time/time.o time/clock_gettime.o time/gmtime.o time/strftime.o string/strpbrk.o ctype/isdigit.o ctype/isalpha.o ctype/isxdigit.o ctype/ispunct.o stdio/setvbuf.o stdio/fileno.o stdio/putc.o stdio/sprintf.o stdlib/abs.o string/strspn.o stdlib/qsort.o string/memmove.o setjmp/longjmp.o setjmp/setjmp.o libgen/basename.o string/strdup.o string/strndup.o string/strlcpy.o stdlib/atexit.o stdio/open_memstream.o libgen/dirname.o unistd/unlink.o string/strstr.o string/strcasecmp.o string/strncasecmp.o stdlib/mkstemp.o string/strtok.o unistd/execvp.o unistd/_exit.o ctype/isalnum.o time/ctime_r.o stdlib/strtold.o sys/time/gettimeofday.o stdio/fgetpos.o stdio/fsetpos.o ctype/isprint.o stdlib/system.o stdio/tmpnam.o unistd/msleep.o stdlib/atof.o stdlib/strtod.o stdio/rename.o sys/stat/mkdir.o unistd/uptime.o
+#OBJ=crt0.o libc.o malloc/malloc.o pty.o sys/mman/mmap.o memset.o assert.o stdio/snprintf.o stdio/vfprintf.o string/memcpy.o string/memcmp.o string/strcmp.o ubsan.o string/strcpy.o isspace.o stdio/puts.o stdio/putchar.o dirent/opendir.o dirent/readdir.o dirent/closedir.o unistd/getopt.o dirent/scandir.o dirent/alphasort.o stdio/printf.o stdio/vdprintf.o stdio/vprintf.o stdio/dprintf.o stdio/vprintf.o string/strlen.o string/strnlen.o stdio/stdin.o stdio/stdout.o stdio/stderr.o stdio/getchar.o stdio/fgetc.o arpa/inet/htons.o arpa/inet/htonl.o stdio/fread.o stdio/fwrite.o stdio/fopen.o stdio/fclose.o stdio/fseek.o ctype/isascii.o stdio/fprintf.o stdlib/atoi.o stdlib/strtol.o ctype/toupper.o ctype/tolower.o string/strcat.o string/strchr.o string/sscanf.o sys/stat/stat.o stdlib/getenv.o string/strrchr.o stdio/ftell.o stdio/tmpfile.o stdio/fgets.o stdio/feof.o stdio/fscanf.o stdio/ungetc.o string/strncmp.o stdio/fputc.o string/strncpy.o stdio/remove.o stdio/ferror.o stdio/fputs.o stdlib/rand.o stdlib/srand.o unistd/getpid.o stdlib/strtoul.o stdio/fflush.o stdlib/abort.o string/strcspn.o time/localtime.o time/time.o time/clock_gettime.o time/gmtime.o time/strftime.o string/strpbrk.o ctype/isdigit.o ctype/isalpha.o ctype/isxdigit.o ctype/ispunct.o stdio/setvbuf.o stdio/fileno.o stdio/putc.o stdio/sprintf.o stdlib/abs.o string/strspn.o stdlib/qsort.o string/memmove.o setjmp/longjmp.o setjmp/setjmp.o
+all: libc.a
+
+%.o: %.c
+ $(CC) $(CFLAGS) -O0 -I. -o $@ -c $< -lgcc
+
+%.o: %.s
+ $(AS) $< -o $@
+
+libc.a: $(OBJ)
+ $(AR) rcs libc.a $(OBJ)
+
+install:
+ cp crt0.o /home/anton/prj/osdev/sysroot/lib/
+ cp libc.a /home/anton/prj/osdev/sysroot/lib/
+ cp -r include /home/anton/prj/osdev/sysroot/usr/
+
+clean:
+ rm libc.a *.o */*.o */*/*.o
diff --git a/userland/libc/arpa/inet.h b/userland/libc/arpa/inet.h
new file mode 100644
index 0000000..7a033f2
--- /dev/null
+++ b/userland/libc/arpa/inet.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+uint32_t htonl(uint32_t hostlong);
+uint16_t htons(uint16_t hostlong);
diff --git a/userland/libc/arpa/inet/htonl.c b/userland/libc/arpa/inet/htonl.c
new file mode 100644
index 0000000..4b66747
--- /dev/null
+++ b/userland/libc/arpa/inet/htonl.c
@@ -0,0 +1,11 @@
+#include <arpa/inet.h>
+#include <endian.h>
+#include <stdint.h>
+
+uint32_t htonl(uint32_t hostlong) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ hostlong = (uint32_t)(htons(hostlong >> 16)) |
+ (uint32_t)(htons(hostlong & 0xFFFF) << 16);
+#endif
+ return hostlong;
+}
diff --git a/userland/libc/arpa/inet/htons.c b/userland/libc/arpa/inet/htons.c
new file mode 100644
index 0000000..798a64a
--- /dev/null
+++ b/userland/libc/arpa/inet/htons.c
@@ -0,0 +1,10 @@
+#include <arpa/inet.h>
+#include <endian.h>
+#include <stdint.h>
+
+uint16_t htons(uint16_t hostlong) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ hostlong = ((hostlong & 0xFF00) >> 8) | ((hostlong & 0x00FF) << 8);
+#endif
+ return hostlong;
+}
diff --git a/userland/libc/assert.c b/userland/libc/assert.c
new file mode 100644
index 0000000..4082f64
--- /dev/null
+++ b/userland/libc/assert.c
@@ -0,0 +1,9 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void aFailed(char *f, int l) {
+ printf("Assert failed\n");
+ printf("%s : %d\n", f, l);
+ exit(1);
+}
diff --git a/userland/libc/assert.h b/userland/libc/assert.h
new file mode 100644
index 0000000..a009e5f
--- /dev/null
+++ b/userland/libc/assert.h
@@ -0,0 +1,10 @@
+#ifndef ASSERT_H
+#define ASSERT_H
+
+#define assert(expr) \
+ { \
+ if (!(expr)) \
+ aFailed(__FILE__, __LINE__); \
+ }
+void aFailed(char *f, int l);
+#endif
diff --git a/userland/libc/crt0.s b/userland/libc/crt0.s
new file mode 100644
index 0000000..05144d4
--- /dev/null
+++ b/userland/libc/crt0.s
@@ -0,0 +1,13 @@
+.global _start
+.extern main
+_start:
+ call main
+ mov %eax, %ebx
+ mov $8, %eax
+ int $0x80
+l:
+ nop
+ nop
+ nop
+ nop
+ jmp l
diff --git a/userland/libc/ctype.h b/userland/libc/ctype.h
new file mode 100644
index 0000000..5be4a89
--- /dev/null
+++ b/userland/libc/ctype.h
@@ -0,0 +1,12 @@
+#ifndef CTYPE_H
+#define CTYPE_H
+
+int isspace(int c);
+int isascii(int c);
+int toupper(int c);
+int tolower(int c);
+int isdigit(int c);
+int isalpha(int c);
+int isxdigit(int c);
+int ispunct(int c);
+#endif
diff --git a/userland/libc/ctype/isalnum.c b/userland/libc/ctype/isalnum.c
new file mode 100644
index 0000000..870728d
--- /dev/null
+++ b/userland/libc/ctype/isalnum.c
@@ -0,0 +1,6 @@
+#include <ctype.h>
+
+// This is probably the most useless libc function I have seen so far.
+int isalnum(int c) {
+ return isalpha(c) || isdigit(c);
+}
diff --git a/userland/libc/ctype/isalpha.c b/userland/libc/ctype/isalpha.c
new file mode 100644
index 0000000..130f493
--- /dev/null
+++ b/userland/libc/ctype/isalpha.c
@@ -0,0 +1,4 @@
+#include <ctype.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/isalpha.html
+int isalpha(int c) { return (('A' <= toupper(c)) && ('Z' >= toupper(c))); }
diff --git a/userland/libc/ctype/isascii.c b/userland/libc/ctype/isascii.c
new file mode 100644
index 0000000..660c8bb
--- /dev/null
+++ b/userland/libc/ctype/isascii.c
@@ -0,0 +1,4 @@
+#include <ctype.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/isascii.html
+int isascii(int c) { return (c < 128); }
diff --git a/userland/libc/ctype/isdigit.c b/userland/libc/ctype/isdigit.c
new file mode 100644
index 0000000..790b9f1
--- /dev/null
+++ b/userland/libc/ctype/isdigit.c
@@ -0,0 +1,6 @@
+#include <ctype.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/isdigit.html
+int isdigit(int c) {
+ return ('0' <= c && c <= '9');
+}
diff --git a/userland/libc/ctype/isprint.c b/userland/libc/ctype/isprint.c
new file mode 100644
index 0000000..e6a3d0e
--- /dev/null
+++ b/userland/libc/ctype/isprint.c
@@ -0,0 +1,3 @@
+#include <ctype.h>
+
+int isprint(int c) { return c > 0x20 && 0x7F != c; }
diff --git a/userland/libc/ctype/ispunct.c b/userland/libc/ctype/ispunct.c
new file mode 100644
index 0000000..18dc7d8
--- /dev/null
+++ b/userland/libc/ctype/ispunct.c
@@ -0,0 +1,6 @@
+#include <ctype.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ispunct.html
+int ispunct(int c) {
+ return (c == '.');
+}
diff --git a/userland/libc/ctype/isxdigit.c b/userland/libc/ctype/isxdigit.c
new file mode 100644
index 0000000..c843725
--- /dev/null
+++ b/userland/libc/ctype/isxdigit.c
@@ -0,0 +1,6 @@
+#include <ctype.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/isxdigit.html
+int isxdigit(int c) {
+ return isdigit(c) || ('A' >= toupper(c) && 'Z' <= toupper(c));
+}
diff --git a/userland/libc/ctype/tolower.c b/userland/libc/ctype/tolower.c
new file mode 100644
index 0000000..f1bb163
--- /dev/null
+++ b/userland/libc/ctype/tolower.c
@@ -0,0 +1,7 @@
+#include <ctype.h>
+
+int tolower(int c) {
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 'a';
+ return c;
+}
diff --git a/userland/libc/ctype/toupper.c b/userland/libc/ctype/toupper.c
new file mode 100644
index 0000000..0f33886
--- /dev/null
+++ b/userland/libc/ctype/toupper.c
@@ -0,0 +1,7 @@
+#include <ctype.h>
+
+int toupper(int c) {
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 'A';
+ return c;
+}
diff --git a/userland/libc/dirent.h b/userland/libc/dirent.h
new file mode 100644
index 0000000..f190a7c
--- /dev/null
+++ b/userland/libc/dirent.h
@@ -0,0 +1,28 @@
+#ifndef DIRENT_H
+#define DIRENT_H
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct dirent {
+ ino_t d_ino; // File serial number.
+ char d_name[PATH_MAX]; // Filename string of entry.
+};
+
+typedef struct {
+ int fd;
+ struct dirent internal_direntry;
+ int dir_num;
+} DIR;
+
+DIR *opendir(const char *dirname);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dirp);
+int alphasort(const struct dirent **d1, const struct dirent **d2);
+int scandir(const char *dir, struct dirent ***namelist,
+ int (*sel)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **));
+#endif
diff --git a/userland/libc/dirent/alphasort.c b/userland/libc/dirent/alphasort.c
new file mode 100644
index 0000000..43a4b5f
--- /dev/null
+++ b/userland/libc/dirent/alphasort.c
@@ -0,0 +1,7 @@
+#include <dirent.h>
+
+int alphasort(const struct dirent **d1, const struct dirent **d2) {
+ // TODO: Actually sort it
+ *d2 = *d1;
+ return 0;
+}
diff --git a/userland/libc/dirent/closedir.c b/userland/libc/dirent/closedir.c
new file mode 100644
index 0000000..e216d7c
--- /dev/null
+++ b/userland/libc/dirent/closedir.c
@@ -0,0 +1,7 @@
+#include <dirent.h>
+
+int closedir(DIR *dir) {
+ close(dir->fd);
+ free(dir);
+ return 0;
+}
diff --git a/userland/libc/dirent/opendir.c b/userland/libc/dirent/opendir.c
new file mode 100644
index 0000000..7bfa562
--- /dev/null
+++ b/userland/libc/dirent/opendir.c
@@ -0,0 +1,11 @@
+#include <dirent.h>
+
+DIR *opendir(const char *dirname) {
+ int fd = open(dirname, O_RDONLY, 0);
+ if (-1 == fd)
+ return NULL;
+ DIR *rc = malloc(sizeof(DIR));
+ rc->fd = fd;
+ rc->dir_num = 0;
+ return rc;
+}
diff --git a/userland/libc/dirent/readdir.c b/userland/libc/dirent/readdir.c
new file mode 100644
index 0000000..88aff48
--- /dev/null
+++ b/userland/libc/dirent/readdir.c
@@ -0,0 +1,14 @@
+#include <dirent.h>
+
+struct dirent *readdir(DIR *dir) {
+ size_t offset = dir->dir_num * sizeof(struct dirent);
+ int rc;
+ if (-1 == (rc = pread(dir->fd, &dir->internal_direntry, sizeof(struct dirent),
+ offset)))
+ return NULL;
+ if (rc < sizeof(struct dirent))
+ return NULL;
+
+ dir->dir_num++;
+ return &(dir->internal_direntry);
+}
diff --git a/userland/libc/dirent/scandir.c b/userland/libc/dirent/scandir.c
new file mode 100644
index 0000000..1520140
--- /dev/null
+++ b/userland/libc/dirent/scandir.c
@@ -0,0 +1,43 @@
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+int nop_sel(const struct dirent *unused) {
+ (void)unused;
+ return 1;
+}
+
+int nop_compar(const struct dirent **d1, const struct dirent **d2) {
+ *d2 = *d1;
+ return 0;
+}
+
+int scandir(const char *dir, struct dirent ***namelist,
+ int (*sel)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **)) {
+ if (!sel)
+ sel = nop_sel;
+
+ if (!compar)
+ compar = nop_compar;
+
+ DIR *d = opendir(dir);
+ struct dirent **list = NULL;
+ struct dirent *e;
+ int rc = 0;
+ for (; (e = readdir(d));) {
+ if (!sel(e))
+ continue;
+ struct dirent *p = malloc(sizeof(struct dirent));
+ memcpy(p, e, sizeof(struct dirent));
+ list = realloc(list, (rc + 1) * sizeof(struct dirent *));
+ list[rc] = p;
+ rc++;
+ }
+ // struct dirent **new_list;
+ // compar((const struct dirent **)list, (const struct dirent **)new_list);
+ // *namelist = new_list;
+ *namelist = list;
+ // closedir(d);
+ return rc;
+}
diff --git a/userland/libc/endian.h b/userland/libc/endian.h
new file mode 100644
index 0000000..f265a67
--- /dev/null
+++ b/userland/libc/endian.h
@@ -0,0 +1,2 @@
+#define LITTLE_ENDIAN 0
+#define BYTE_ORDER LITTLE_ENDIAN
diff --git a/userland/libc/errno.h b/userland/libc/errno.h
new file mode 100644
index 0000000..ec9a25e
--- /dev/null
+++ b/userland/libc/errno.h
@@ -0,0 +1,87 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+#define E2BIG 1 // Argument list too long.
+#define EACCES 2 // Permission denied.
+#define EADDRINUSE 3 // Address in use.
+#define EADDRNOTAVAIL 4 // Address not available.
+#define EAFNOSUPPORT 5 // Address family not supported.
+#define EAGAIN 6 // Resource unavailable, try again.
+#define EALREADY 7 // Connection already in progress.
+#define EBADF 8 // Bad file descriptor.
+#define EBADMSG 9 // Bad message.
+#define EBUSY 10 // Device or resource busy.
+#define ECANCELED 11 // Operation canceled.
+#define ECHILD 12 // No child processes.
+#define ECONNABORTED 13 // Connection aborted.
+#define ECONNREFUSED 14 // Connection refused.
+#define ECONNRESET 15 // Connection reset.
+#define EDEADLK 16 // Resource deadlock would occur.
+#define EDESTADDRREQ 17 // Destination address required.
+#define EDOM 18 // Mathematics argument out of domain of function.
+#define EDQUOT 19 // Reserved.
+#define EEXIST 20 // File exists.
+#define EFAULT 21 // Bad address.
+#define EFBIG 22 // File too large.
+#define EHOSTUNREACH 23 // Host is unreachable.
+#define EIDRM 24 // Identifier removed.
+#define EILSEQ 25 // Illegal byte sequence.
+#define EINPROGRESS 26 // Operation in progress.
+#define EINTR 27 // Interrupted function.
+#define EINVAL 28 // Invalid argument.
+#define EIO 29 // I/O error.
+#define EISCONN 30 // Socket is connected.
+#define EISDIR 31 // Is a directory.
+#define ELOOP 32 // Too many levels of symbolic links.
+#define EMFILE 33 // File descriptor value too large.
+#define EMLINK 34 // Too many links.
+#define EMSGSIZE 35 // Message too large.
+#define EMULTIHOP 36 // Reserved.
+#define ENAMETOOLONG 37 // Filename too long.
+#define ENETDOWN 38 // Network is down.
+#define ENETRESET 39 // Connection aborted by network.
+#define ENETUNREACH 40 // Network unreachable.
+#define ENFILE 41 // Too many files open in system.
+#define ENOBUFS 42 // No buffer space available.
+#define ENODATA 43 // No message is available on the STREAM head read queue.
+#define ENODEV 44 // No such device.
+#define ENOENT 45 // No such file or directory.
+#define ENOEXEC 46 // Executable file format error.
+#define ENOLCK 47 // No locks available.
+#define ENOLINK 48 // Reserved.
+#define ENOMEM 49 // Not enough space.
+#define ENOMSG 50 // No message of the desired type.
+#define ENOPROTOOPT 51 // Protocol not available.
+#define ENOSPC 52 // No space left on device.
+#define ENOSR 53 // No STREAM resources.
+#define ENOSTR 54 // Not a STREAM.
+#define ENOSYS 55 // Functionality not supported.
+#define ENOTCONN 56 // The socket is not connected.
+#define ENOTDIR 57 // Not a directory or a symbolic link to a directory.
+#define ENOTEMPTY 58 // Directory not empty.
+#define ENOTRECOVERABLE 59 // State not recoverable.
+#define ENOTSOCK 60 // Not a socket.
+#define ENOTSUP 61 // Not supported (may be the same value as.
+#define ENOTTY 62 // Inappropriate I/O control operation.
+#define ENXIO 63 // No such device or address.
+#define EOPNOTSUPP ENOTSUP // Operation not supported on socket.
+#define EOVERFLOW 65 // Value too large to be stored in data type.
+#define EOWNERDEAD 66 // Previous owner died.
+#define EPERM 67 // Operation not permitted.
+#define EPIPE 68 // Broken pipe.
+#define EPROTO 69 // Protocol error.
+#define EPROTONOSUPPORT 70 // Protocol not supported.
+#define EPROTOTYPE 71 // Protocol wrong type for socket.
+#define ERANGE 72 // Result too large.
+#define EROFS 73 // Read-only file system.
+#define ESPIPE 74 // Invalid seek.
+#define ESRCH 75 // No such process.
+#define ESTALE 76 // Reserved.
+#define ETIME 77 // Stream ioctl() timeout.
+#define ETIMEDOUT 78 // Connection timed out.
+#define ETXTBSY 79 // Text file busy.
+#define EWOULDBLOCK EAGAIN // Operation would block.
+#define EXDEV 81 // Cross-device link.
+
+extern int errno;
+#endif
diff --git a/userland/libc/fcntl.h b/userland/libc/fcntl.h
new file mode 100644
index 0000000..4c1eb9c
--- /dev/null
+++ b/userland/libc/fcntl.h
@@ -0,0 +1,10 @@
+// FIXME: Is there some standard value for this?
+#define O_NONBLOCK (1 << 0)
+#define O_READ (1 << 1)
+#define O_WRITE (1 << 2)
+#define O_CREAT (1 << 3)
+#define O_RDONLY O_READ
+#define O_WRONLY O_WRITE
+#define O_RDWR (O_WRITE | O_READ)
+
+int open(const char *file, int flags, int mode);
diff --git a/userland/libc/include/arpa/inet.h b/userland/libc/include/arpa/inet.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/arpa/inet.h
diff --git a/userland/libc/include/assert.h b/userland/libc/include/assert.h
new file mode 100644
index 0000000..a009e5f
--- /dev/null
+++ b/userland/libc/include/assert.h
@@ -0,0 +1,10 @@
+#ifndef ASSERT_H
+#define ASSERT_H
+
+#define assert(expr) \
+ { \
+ if (!(expr)) \
+ aFailed(__FILE__, __LINE__); \
+ }
+void aFailed(char *f, int l);
+#endif
diff --git a/userland/libc/include/byteswap.h b/userland/libc/include/byteswap.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/byteswap.h
diff --git a/userland/libc/include/ctype.h b/userland/libc/include/ctype.h
new file mode 100644
index 0000000..7c8c311
--- /dev/null
+++ b/userland/libc/include/ctype.h
@@ -0,0 +1,14 @@
+#ifndef CTYPE_H
+#define CTYPE_H
+
+int isspace(int c);
+int isascii(int c);
+int toupper(int c);
+int tolower(int c);
+int isdigit(int c);
+int isalpha(int c);
+int isxdigit(int c);
+int ispunct(int c);
+int isalnum(int c);
+int isprint(int c);
+#endif
diff --git a/userland/libc/include/dirent.h b/userland/libc/include/dirent.h
new file mode 100644
index 0000000..f190a7c
--- /dev/null
+++ b/userland/libc/include/dirent.h
@@ -0,0 +1,28 @@
+#ifndef DIRENT_H
+#define DIRENT_H
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct dirent {
+ ino_t d_ino; // File serial number.
+ char d_name[PATH_MAX]; // Filename string of entry.
+};
+
+typedef struct {
+ int fd;
+ struct dirent internal_direntry;
+ int dir_num;
+} DIR;
+
+DIR *opendir(const char *dirname);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dirp);
+int alphasort(const struct dirent **d1, const struct dirent **d2);
+int scandir(const char *dir, struct dirent ***namelist,
+ int (*sel)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **));
+#endif
diff --git a/userland/libc/include/endian.h b/userland/libc/include/endian.h
new file mode 100644
index 0000000..f265a67
--- /dev/null
+++ b/userland/libc/include/endian.h
@@ -0,0 +1,2 @@
+#define LITTLE_ENDIAN 0
+#define BYTE_ORDER LITTLE_ENDIAN
diff --git a/userland/libc/include/err.h b/userland/libc/include/err.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/err.h
diff --git a/userland/libc/include/errno.h b/userland/libc/include/errno.h
new file mode 100644
index 0000000..ec9a25e
--- /dev/null
+++ b/userland/libc/include/errno.h
@@ -0,0 +1,87 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+#define E2BIG 1 // Argument list too long.
+#define EACCES 2 // Permission denied.
+#define EADDRINUSE 3 // Address in use.
+#define EADDRNOTAVAIL 4 // Address not available.
+#define EAFNOSUPPORT 5 // Address family not supported.
+#define EAGAIN 6 // Resource unavailable, try again.
+#define EALREADY 7 // Connection already in progress.
+#define EBADF 8 // Bad file descriptor.
+#define EBADMSG 9 // Bad message.
+#define EBUSY 10 // Device or resource busy.
+#define ECANCELED 11 // Operation canceled.
+#define ECHILD 12 // No child processes.
+#define ECONNABORTED 13 // Connection aborted.
+#define ECONNREFUSED 14 // Connection refused.
+#define ECONNRESET 15 // Connection reset.
+#define EDEADLK 16 // Resource deadlock would occur.
+#define EDESTADDRREQ 17 // Destination address required.
+#define EDOM 18 // Mathematics argument out of domain of function.
+#define EDQUOT 19 // Reserved.
+#define EEXIST 20 // File exists.
+#define EFAULT 21 // Bad address.
+#define EFBIG 22 // File too large.
+#define EHOSTUNREACH 23 // Host is unreachable.
+#define EIDRM 24 // Identifier removed.
+#define EILSEQ 25 // Illegal byte sequence.
+#define EINPROGRESS 26 // Operation in progress.
+#define EINTR 27 // Interrupted function.
+#define EINVAL 28 // Invalid argument.
+#define EIO 29 // I/O error.
+#define EISCONN 30 // Socket is connected.
+#define EISDIR 31 // Is a directory.
+#define ELOOP 32 // Too many levels of symbolic links.
+#define EMFILE 33 // File descriptor value too large.
+#define EMLINK 34 // Too many links.
+#define EMSGSIZE 35 // Message too large.
+#define EMULTIHOP 36 // Reserved.
+#define ENAMETOOLONG 37 // Filename too long.
+#define ENETDOWN 38 // Network is down.
+#define ENETRESET 39 // Connection aborted by network.
+#define ENETUNREACH 40 // Network unreachable.
+#define ENFILE 41 // Too many files open in system.
+#define ENOBUFS 42 // No buffer space available.
+#define ENODATA 43 // No message is available on the STREAM head read queue.
+#define ENODEV 44 // No such device.
+#define ENOENT 45 // No such file or directory.
+#define ENOEXEC 46 // Executable file format error.
+#define ENOLCK 47 // No locks available.
+#define ENOLINK 48 // Reserved.
+#define ENOMEM 49 // Not enough space.
+#define ENOMSG 50 // No message of the desired type.
+#define ENOPROTOOPT 51 // Protocol not available.
+#define ENOSPC 52 // No space left on device.
+#define ENOSR 53 // No STREAM resources.
+#define ENOSTR 54 // Not a STREAM.
+#define ENOSYS 55 // Functionality not supported.
+#define ENOTCONN 56 // The socket is not connected.
+#define ENOTDIR 57 // Not a directory or a symbolic link to a directory.
+#define ENOTEMPTY 58 // Directory not empty.
+#define ENOTRECOVERABLE 59 // State not recoverable.
+#define ENOTSOCK 60 // Not a socket.
+#define ENOTSUP 61 // Not supported (may be the same value as.
+#define ENOTTY 62 // Inappropriate I/O control operation.
+#define ENXIO 63 // No such device or address.
+#define EOPNOTSUPP ENOTSUP // Operation not supported on socket.
+#define EOVERFLOW 65 // Value too large to be stored in data type.
+#define EOWNERDEAD 66 // Previous owner died.
+#define EPERM 67 // Operation not permitted.
+#define EPIPE 68 // Broken pipe.
+#define EPROTO 69 // Protocol error.
+#define EPROTONOSUPPORT 70 // Protocol not supported.
+#define EPROTOTYPE 71 // Protocol wrong type for socket.
+#define ERANGE 72 // Result too large.
+#define EROFS 73 // Read-only file system.
+#define ESPIPE 74 // Invalid seek.
+#define ESRCH 75 // No such process.
+#define ESTALE 76 // Reserved.
+#define ETIME 77 // Stream ioctl() timeout.
+#define ETIMEDOUT 78 // Connection timed out.
+#define ETXTBSY 79 // Text file busy.
+#define EWOULDBLOCK EAGAIN // Operation would block.
+#define EXDEV 81 // Cross-device link.
+
+extern int errno;
+#endif
diff --git a/userland/libc/include/fcntl.h b/userland/libc/include/fcntl.h
new file mode 100644
index 0000000..7f0906d
--- /dev/null
+++ b/userland/libc/include/fcntl.h
@@ -0,0 +1,12 @@
+// FIXME: Is there some standard value for this?
+#define O_NONBLOCK (1 << 0)
+#define O_READ (1 << 1)
+#define O_WRITE (1 << 2)
+#define O_CREAT (1 << 3)
+#define O_TRUNC (1 << 4)
+
+#define O_RDONLY O_READ
+#define O_WRONLY O_WRITE
+#define O_RDWR (O_WRITE | O_READ)
+
+int open(const char *file, int flags, int mode);
diff --git a/userland/libc/include/fnmatch.h b/userland/libc/include/fnmatch.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/fnmatch.h
diff --git a/userland/libc/include/glob.h b/userland/libc/include/glob.h
new file mode 100644
index 0000000..f9e9c57
--- /dev/null
+++ b/userland/libc/include/glob.h
@@ -0,0 +1,10 @@
+#ifndef GLOB_H
+#define GLOB_H
+#include <stddef.h>
+
+typedef struct {
+ size_t gl_pathc; // Count of paths matched by pattern.
+ char **gl_pathv; // Pointer to a list of matched pathnames
+ size_t gl_offs; // Slots to reserve at the beginning of gl_pathv.
+} glob_t;
+#endif
diff --git a/userland/libc/include/grp.h b/userland/libc/include/grp.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/grp.h
diff --git a/userland/libc/include/input.h b/userland/libc/include/input.h
new file mode 100644
index 0000000..a6602f5
--- /dev/null
+++ b/userland/libc/include/input.h
@@ -0,0 +1,9 @@
+#ifndef INPUT_H
+#define INPUT_H
+#include <stdint.h>
+
+struct input_event {
+ uint16_t key;
+ uint8_t status;
+};
+#endif
diff --git a/userland/libc/include/inttypes.h b/userland/libc/include/inttypes.h
new file mode 100644
index 0000000..adde7c8
--- /dev/null
+++ b/userland/libc/include/inttypes.h
@@ -0,0 +1,19 @@
+#include <stdint.h>
+
+// FIXME: These are not correct
+#define PRId8 "d"
+#define PRId16 "d"
+#define PRId32 "d"
+#define PRId64 "d"
+#define PRIu8 "d"
+#define PRIu16 "d"
+#define PRIu32 "d"
+#define PRIu64 "d"
+#define PRIx8 "x"
+#define PRIx16 "x"
+#define PRIx32 "x"
+#define PRIx64 "x"
+#define PRIX8 "x"
+#define PRIX16 "x"
+#define PRIX32 "x"
+#define PRIX64 "x"
diff --git a/userland/libc/include/langinfo.h b/userland/libc/include/langinfo.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/langinfo.h
diff --git a/userland/libc/include/libgen.h b/userland/libc/include/libgen.h
new file mode 100644
index 0000000..16dc3ed
--- /dev/null
+++ b/userland/libc/include/libgen.h
@@ -0,0 +1,6 @@
+#ifndef LIBGEN_H
+#define LIBGEN_H
+
+char *basename(char *path);
+char *dirname(char *path);
+#endif
diff --git a/userland/libc/include/limits.h b/userland/libc/include/limits.h
new file mode 100644
index 0000000..6d0abe0
--- /dev/null
+++ b/userland/libc/include/limits.h
@@ -0,0 +1,4 @@
+#define PATH_MAX 256
+#define FILENAME_MAX PATH_MAX
+#define ULONG_MAX 0xFFFFFFFFUL
+#define LONG_MAX 2147483647
diff --git a/userland/libc/include/locale.h b/userland/libc/include/locale.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/locale.h
diff --git a/userland/libc/include/math.h b/userland/libc/include/math.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/math.h
diff --git a/userland/libc/include/net/if.h b/userland/libc/include/net/if.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/net/if.h
diff --git a/userland/libc/include/netdb.h b/userland/libc/include/netdb.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/netdb.h
diff --git a/userland/libc/include/netinet/in.h b/userland/libc/include/netinet/in.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/netinet/in.h
diff --git a/userland/libc/include/netinet/tcp.h b/userland/libc/include/netinet/tcp.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/netinet/tcp.h
diff --git a/userland/libc/include/paths.h b/userland/libc/include/paths.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/paths.h
diff --git a/userland/libc/include/poll.h b/userland/libc/include/poll.h
new file mode 100644
index 0000000..e3c6d8d
--- /dev/null
+++ b/userland/libc/include/poll.h
@@ -0,0 +1,17 @@
+#ifndef POLL_H
+#define POLL_H
+#include <stddef.h>
+
+#define POLLIN (1 << 0)
+#define POLLPRI (1 << 1)
+#define POLLOUT (1 << 2)
+#define POLLHUP (1 << 3)
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+int poll(struct pollfd *fds, size_t nfds, int timeout);
+#endif
diff --git a/userland/libc/include/pty.h b/userland/libc/include/pty.h
new file mode 100644
index 0000000..b8ce978
--- /dev/null
+++ b/userland/libc/include/pty.h
@@ -0,0 +1,6 @@
+#ifndef PTY_H
+#define PTY_H
+int openpty(int *amaster, int *aslave, char *name,
+ /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp);
+#endif
diff --git a/userland/libc/include/pwd.h b/userland/libc/include/pwd.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/pwd.h
diff --git a/userland/libc/include/regex.h b/userland/libc/include/regex.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/regex.h
diff --git a/userland/libc/include/sched.h b/userland/libc/include/sched.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sched.h
diff --git a/userland/libc/include/setjmp.h b/userland/libc/include/setjmp.h
new file mode 100644
index 0000000..ea15cf3
--- /dev/null
+++ b/userland/libc/include/setjmp.h
@@ -0,0 +1,14 @@
+#ifndef SETJMP_H
+#define SETJMP_H
+typedef unsigned long __jmp_buf[6];
+typedef struct __jmp_buf_tag {
+ __jmp_buf __jb;
+ unsigned long __fl;
+ unsigned long __ss[128/sizeof(long)];
+} jmp_buf[1];
+typedef jmp_buf sigjmp_buf;
+
+void _longjmp(jmp_buf, int);
+void longjmp(jmp_buf, int);
+void siglongjmp(sigjmp_buf, int);
+#endif
diff --git a/userland/libc/include/signal.h b/userland/libc/include/signal.h
new file mode 100644
index 0000000..2e6566d
--- /dev/null
+++ b/userland/libc/include/signal.h
@@ -0,0 +1,9 @@
+#ifndef SIGNAL_H
+#define SIGNAL_H
+#define SIGHUP 0
+#define SIGINT 1
+#define SIGWINCH 2
+#define SIGQUIT 3
+#define SIG_IGN 4
+typedef int sig_atomic_t;
+#endif // SIGNAL_H
diff --git a/userland/libc/include/socket.h b/userland/libc/include/socket.h
new file mode 100644
index 0000000..5e86b45
--- /dev/null
+++ b/userland/libc/include/socket.h
@@ -0,0 +1,41 @@
+#include <stddef.h>
+#include <stdint.h>
+
+#define AF_UNIX 0
+#define AF_LOCAL AF_UNIX
+
+#define INADDR_ANY 0
+
+typedef struct {
+ int domain;
+ int type;
+ int protocol;
+
+ // UNIX socket
+ char *path;
+ int incoming_fd;
+} SOCKET;
+
+typedef struct {
+ char *path;
+ SOCKET *s;
+} OPEN_UNIX_SOCKET;
+
+typedef uint32_t in_addr_t;
+typedef uint16_t in_port_t;
+typedef unsigned int sa_family_t;
+typedef uint32_t socklen_t;
+
+struct sockaddr {
+ sa_family_t sa_family; /* Address family */
+ char *sa_data; /* Socket address */
+};
+
+struct sockaddr_un {
+ sa_family_t sun_family; /* Address family */
+ char *sun_path; /* Socket pathname */
+};
+
+int socket(int domain, int type, int protocol);
+int accept(int socket, struct sockaddr *address, socklen_t *address_len);
+int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
diff --git a/userland/libc/include/stdio.h b/userland/libc/include/stdio.h
new file mode 100644
index 0000000..014e2ac
--- /dev/null
+++ b/userland/libc/include/stdio.h
@@ -0,0 +1,116 @@
+#ifndef STDIO_H
+#define STDIO_H
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// FIXME: Most of these should probably not be here. But I am too lazy
+// to fix it right now. This is futures mees problem to deal wth.
+
+#define EOF (-1)
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+#define BUFSIZ 4096
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setvbuf.html
+#define _IOFBF 1 // shall cause input/output to be fully buffered.
+#define _IOLBF 2 // shall cause input/output to be line buffered.
+#define _IONBF 3 // shall cause input/output to be unbuffered.
+
+typedef long fpos_t;
+
+typedef struct __IO_FILE FILE;
+struct __IO_FILE {
+ size_t (*write)(FILE *, const unsigned char *, size_t);
+ size_t (*read)(FILE *, unsigned char *, size_t);
+ int (*seek)(FILE *, long, int);
+ long offset_in_file;
+ int buffered_char;
+ int has_buffered_char;
+ int fd;
+ uint8_t is_eof;
+ uint8_t has_error;
+ uint64_t file_size;
+ void *cookie;
+};
+
+size_t write_fd(FILE *f, const unsigned char *s, size_t l);
+size_t read_fd(FILE *f, unsigned char *s, size_t l);
+int seek_fd(FILE *stream, long offset, int whence);
+
+typedef struct {
+ int fd;
+} FILE_FD_COOKIE;
+
+extern FILE __stdin_FILE;
+extern FILE __stdout_FILE;
+extern FILE __stderr_FILE;
+
+#define stdin (&__stdin_FILE)
+#define stdout (&__stdout_FILE)
+//#define stderr (&__stderr_FILE)
+#define stderr (&__stdout_FILE)
+
+typedef int mode_t;
+
+void perror(const char *s);
+
+int putchar(int c);
+int puts(const char *s);
+int brk(void *addr);
+void *sbrk(intptr_t increment);
+int write(int fd, const char *buf, size_t count);
+int pwrite(int fd, const char *buf, size_t count, size_t offset);
+int printf(const char *format, ...);
+int pread(int fd, void *buf, size_t count, size_t offset);
+int read(int fd, void *buf, size_t count);
+int fork(void);
+int memcmp(const void *s1, const void *s2, size_t n);
+int wait(int *stat_loc);
+void exit(int status);
+void *memcpy(void *dest, const void *src, uint32_t n);
+int shm_open(const char *name, int oflag, mode_t mode);
+int dprintf(int fd, const char *format, ...);
+int vdprintf(int fd, const char *format, va_list ap);
+int vprintf(const char *format, va_list ap);
+int snprintf(char *str, size_t size, const char *format, ...);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+int vfprintf(FILE *f, const char *fmt, va_list ap);
+int fgetc(FILE *stream);
+int getchar(void);
+#define getc(_a) fgetc(_a)
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+FILE *fopen(const char *pathname, const char *mode);
+int fclose(FILE *stream);
+int fseek(FILE *stream, long offset, int whence);
+int fprintf(FILE *f, const char *fmt, ...);
+int atoi(const char *str);
+long strtol(const char *nptr, char **endptr, int base);
+char *strchr(const char *s, int c);
+char *strcat(char *s1, const char *s2);
+char *fgets(char *s, int n, FILE *stream);
+FILE *tmpfile(void);
+int feof(FILE *stream);
+int fscanf(FILE *stream, const char *format, ...);
+int ungetc(int c, FILE *stream);
+long ftell(FILE *stream);
+int fputc(int c, FILE *stream);
+int remove(const char *path);
+int ferror(FILE *stream);
+int fputs(const char *s, FILE *stream);
+int fflush(FILE *stream);
+int setvbuf(FILE *stream, char *restrict buf, int type, size_t size);
+int fileno(FILE *stream);
+int putc(int c, FILE *stream);
+int vsprintf(char *str, const char *format, va_list ap);
+int sprintf(char *str, const char *format, ...);
+FILE *open_memstream(char **bufp, size_t *sizep);
+int fsetpos(FILE *stream, const fpos_t *pos);
+int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
+char *tmpnam(char *s);
+int rename(const char *old, const char *new);
+#endif
diff --git a/userland/libc/include/stdlib.h b/userland/libc/include/stdlib.h
new file mode 100644
index 0000000..c88f2f3
--- /dev/null
+++ b/userland/libc/include/stdlib.h
@@ -0,0 +1,32 @@
+#ifndef STDLIB_H
+#define STDLIB_H
+#include <limits.h>
+#include <stddef.h>
+#define RAND_MAX (UINT32_MAX)
+#define EXIT_SUCCESS 0
+#define EXIT_FAILURE 1
+
+typedef size_t size_t; // only for 32 bit
+
+void *malloc(size_t s);
+void *calloc(size_t nelem, size_t elsize);
+void *realloc(void *ptr, size_t size);
+void free(void *p);
+char *getenv(const char *name);
+int rand(void);
+void srand(unsigned int seed);
+unsigned long strtoul(const char *restrict str, char **restrict endptr,
+ int base);
+long strtol(const char *str, char **restrict endptr, int base);
+void abort(void);
+int abs(int i);
+void qsort(void *base, size_t nel, size_t width,
+ int (*compar)(const void *, const void *));
+int atexit(void (*func)(void));
+int mkstemp(char *template);
+long double strtold(const char *restrict nptr, char **restrict endptr);
+int system(const char *command);
+double atof(const char *str);
+double strtod(const char *restrict nptr, char **restrict endptr);
+int atoi(const char *str);
+#endif
diff --git a/userland/libc/include/string.h b/userland/libc/include/string.h
new file mode 100644
index 0000000..0c61efa
--- /dev/null
+++ b/userland/libc/include/string.h
@@ -0,0 +1,29 @@
+#ifndef STRING_H
+#define STRING_H
+#include <stddef.h>
+#include <stdint.h>
+
+char *strerror(int errnum);
+void *memset(void *s, int c, size_t n);
+void *memcpy(void *dest, const void *src, uint32_t n);
+int strcmp(const char *s1, const char *s2);
+char *strcpy(char *dest, const char *src);
+size_t strlen(const char *s);
+size_t strnlen(const char *s, size_t maxlen);
+int sscanf(const char *s, const char *restrict format, ...);
+char *strrchr(const char *s, int c);
+int strncmp(const char *s1, const char *s2, size_t n);
+char *strncpy(char *s1, const char *s2, size_t n);
+size_t *strlcpy(char *s1, const char *s2, size_t n);
+size_t strcspn(const char *s1, const char *s2);
+char *strpbrk(const char *s1, const char *s2);
+size_t strspn(const char *s1, const char *s2);
+void *memmove(void *s1, const void *s2, size_t n);
+char *strdup(const char *s);
+char *strndup(const char *s, size_t size);
+char *strstr(const char *s1, const char *s2);
+int strncasecmp(const char *s1, const char *s2, size_t n);
+int strcasecmp(const char *s1, const char *s2);
+char *strtok(char *restrict s, const char *restrict sep);
+char *strcat(char *restrict s1, const char *restrict s2);
+#endif
diff --git a/userland/libc/include/strings.h b/userland/libc/include/strings.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/strings.h
diff --git a/userland/libc/include/sys/ioctl.h b/userland/libc/include/sys/ioctl.h
new file mode 100644
index 0000000..a373a4b
--- /dev/null
+++ b/userland/libc/include/sys/ioctl.h
@@ -0,0 +1,10 @@
+#ifndef IOCTL_H
+#define IOCTL_H
+#define TIOCGWINSZ 0
+struct winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel;
+ unsigned short ws_ypixel;
+};
+#endif // IOCTL_H
diff --git a/userland/libc/include/sys/mman.h b/userland/libc/include/sys/mman.h
new file mode 100644
index 0000000..7dfe29a
--- /dev/null
+++ b/userland/libc/include/sys/mman.h
@@ -0,0 +1,15 @@
+#ifndef MMAP_H
+#define MMAP_H
+#include <stdint.h>
+#include <stddef.h>
+
+#define PROT_READ (1 << 0)
+#define PROT_WRITE (1 << 1)
+
+#define MAP_PRIVATE (1 << 0)
+#define MAP_ANONYMOUS (1<< 1)
+#define MAP_SHARED (1<< 2)
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ size_t offset);
+#endif
diff --git a/userland/libc/include/sys/mount.h b/userland/libc/include/sys/mount.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/mount.h
diff --git a/userland/libc/include/sys/resource.h b/userland/libc/include/sys/resource.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/resource.h
diff --git a/userland/libc/include/sys/socket.h b/userland/libc/include/sys/socket.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/socket.h
diff --git a/userland/libc/include/sys/stat.h b/userland/libc/include/sys/stat.h
new file mode 100644
index 0000000..178d014
--- /dev/null
+++ b/userland/libc/include/sys/stat.h
@@ -0,0 +1,31 @@
+#ifndef STAT_H
+#define STAT_H
+#include <sys/types.h>
+#include <time.h>
+
+struct stat {
+ dev_t st_dev; // Device ID of device containing file.
+ ino_t st_ino; // File serial number.
+ mode_t st_mode; // Mode of file (see below).
+ nlink_t st_nlink; // Number of hard links to the file.
+ uid_t st_uid; // User ID of file.
+ gid_t st_gid; // Group ID of file.
+ dev_t st_rdev; // Device ID (if file is character or block special).
+ off_t st_size; // For regular files, the file size in bytes.
+ // For symbolic links, the length in bytes of the
+ // pathname contained in the symbolic link.
+ // For a shared memory object, the length in bytes.
+ // For a typed memory object, the length in bytes.
+ // For other file types, the use of this field is
+ // unspecified.
+ struct timespec st_atime; // Last data access timestamp.
+ struct timespec st_mtime; // Last data modification timestamp.
+ struct timespec st_ctime; // Last file status change timestamp.
+ blksize_t st_blksize; // A file system-specific preferred I/O block size
+ // for this object. In some file system types, this
+ // may vary from file to file.
+ blkcnt_t st_blocks; // Number of blocks allocated for this object.
+};
+int stat(const char *path, struct stat *buf);
+int mkdir(const char *path, mode_t mode);
+#endif
diff --git a/userland/libc/include/sys/statvfs.h b/userland/libc/include/sys/statvfs.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/statvfs.h
diff --git a/userland/libc/include/sys/syscall.h b/userland/libc/include/sys/syscall.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/syscall.h
diff --git a/userland/libc/include/sys/time.h b/userland/libc/include/sys/time.h
new file mode 100644
index 0000000..a675d9e
--- /dev/null
+++ b/userland/libc/include/sys/time.h
@@ -0,0 +1,41 @@
+#ifndef TIME_H
+#define TIME_H
+#include <stddef.h>
+#include <sys/types.h>
+#include <time.h>
+
+#define CLOCK_REALTIME 0
+
+struct tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ long __tm_gmtoff;
+ const char *__tm_zone;
+};
+
+typedef int clockid_t;
+struct timespec {
+ time_t tv_sec; // Seconds.
+ long tv_nsec; // Nanoseconds.
+};
+
+struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+};
+
+time_t time(time_t *tloc);
+int clock_gettime(clockid_t clock_id, struct timespec *tp);
+struct tm *localtime(const time_t *timer);
+struct tm *gmtime(const time_t *timer);
+size_t strftime(char *restrict s, size_t maxsize, const char *restrict format,
+ const struct tm *restrict timeptr);
+int gettimeofday(struct timeval *tp, void *tzp);
+#endif
diff --git a/userland/libc/include/sys/times.h b/userland/libc/include/sys/times.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/times.h
diff --git a/userland/libc/include/sys/types.h b/userland/libc/include/sys/types.h
new file mode 100644
index 0000000..48c57f9
--- /dev/null
+++ b/userland/libc/include/sys/types.h
@@ -0,0 +1,28 @@
+#ifndef TYPES_H
+#define TYPES_H
+typedef unsigned int ino_t;
+
+typedef int mode_t;
+
+typedef int nlink_t;
+typedef int uid_t;
+typedef int gid_t;
+typedef int id_t;
+
+typedef int blkcnt_t;
+typedef int off_t;
+
+typedef int dev_t;
+typedef unsigned int fsblkcnt_t;
+typedef unsigned int fsfilcnt_t;
+typedef unsigned int ino_t;
+//typedef unsigned int size_t;
+
+typedef int blksize_t;
+typedef int pid_t;
+typedef int ssize_t;
+
+//typedef int clock_t;
+typedef int time_t;
+typedef unsigned int suseconds_t;
+#endif
diff --git a/userland/libc/include/sys/ucontext.h b/userland/libc/include/sys/ucontext.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/ucontext.h
diff --git a/userland/libc/include/sys/un.h b/userland/libc/include/sys/un.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/un.h
diff --git a/userland/libc/include/sys/utsname.h b/userland/libc/include/sys/utsname.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/utsname.h
diff --git a/userland/libc/include/sys/wait.h b/userland/libc/include/sys/wait.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/sys/wait.h
diff --git a/userland/libc/include/syscall.h b/userland/libc/include/syscall.h
new file mode 100644
index 0000000..caa7779
--- /dev/null
+++ b/userland/libc/include/syscall.h
@@ -0,0 +1,150 @@
+#ifndef SYSCALL_H
+#define SYSCALL_H
+#include "socket.h"
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define SYS_OPEN 0
+#define SYS_READ 1
+#define SYS_WRITE 2
+#define SYS_PREAD 3
+#define SYS_PWRITE 4
+#define SYS_FORK 5
+#define SYS_EXEC 6
+#define SYS_GETPID 7
+#define SYS_EXIT 8
+#define SYS_WAIT 9
+#define SYS_BRK 10
+#define SYS_SBRK 11
+#define SYS_PIPE 12
+#define SYS_DUP2 13
+#define SYS_CLOSE 14
+#define SYS_OPENPTY 15
+#define SYS_POLL 16
+#define SYS_MMAP 17
+#define SYS_ACCEPT 18
+#define SYS_BIND 19
+#define SYS_SOCKET 20
+#define SYS_SHM_OPEN 21
+#define SYS_FTRUNCATE 22
+#define SYS_STAT 23
+#define SYS_MSLEEP 24
+#define SYS_UPTIME 25
+
+int syscall(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx,
+ uint32_t esi, uint32_t edi);
+int s_syscall(int sys);
+
+extern int errno;
+#define RC_ERRNO(_rc) \
+ { \
+ int c = _rc; \
+ if (c < 0) { \
+ errno = -(c); \
+ return -1; \
+ } \
+ return c; \
+ }
+
+typedef int mode_t;
+
+typedef struct SYS_OPEN_PARAMS {
+ const char *file;
+ int flags;
+ int mode;
+} __attribute__((packed)) SYS_OPEN_PARAMS;
+
+typedef struct SYS_PREAD_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PREAD_PARAMS;
+
+typedef struct SYS_READ_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_READ_PARAMS;
+
+typedef struct SYS_PWRITE_PARAMS {
+ int fd;
+ const void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PWRITE_PARAMS;
+
+typedef struct SYS_WRITE_PARAMS {
+ int fd;
+ const void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_WRITE_PARAMS;
+
+typedef struct SYS_EXEC_PARAMS {
+ const char *path;
+ char **argv;
+} __attribute__((packed)) SYS_EXEC_PARAMS;
+
+typedef struct SYS_DUP2_PARAMS {
+ int org_fd;
+ int new_fd;
+} __attribute__((packed)) SYS_DUP2_PARAMS;
+
+typedef struct SYS_OPENPTY_PARAMS {
+ int *amaster;
+ int *aslave;
+ char *name;
+ /*const struct termios*/ void *termp;
+ /*const struct winsize*/ void *winp;
+} __attribute__((packed)) SYS_OPENPTY_PARAMS;
+
+typedef struct SYS_POLL_PARAMS {
+ struct pollfd *fds;
+ size_t nfds;
+ int timeout;
+} __attribute__((packed)) SYS_POLL_PARAMS;
+
+typedef struct SYS_MMAP_PARAMS {
+ void *addr;
+ size_t length;
+ int prot;
+ int flags;
+ int fd;
+ size_t offset;
+} __attribute__((packed)) SYS_MMAP_PARAMS;
+
+typedef struct SYS_SOCKET_PARAMS {
+ int domain;
+ int type;
+ int protocol;
+} __attribute__((packed)) SYS_SOCKET_PARAMS;
+
+typedef struct SYS_BIND_PARAMS {
+ int sockfd;
+ const struct sockaddr *addr;
+ socklen_t addrlen;
+} __attribute__((packed)) SYS_BIND_PARAMS;
+
+typedef struct SYS_ACCEPT_PARAMS {
+ int socket;
+ struct sockaddr *address;
+ socklen_t *address_len;
+} __attribute__((packed)) SYS_ACCEPT_PARAMS;
+
+typedef struct SYS_SHM_OPEN_PARAMS {
+ const char *name;
+ int oflag;
+ mode_t mode;
+} __attribute__((packed)) SYS_SHM_OPEN_PARAMS;
+
+typedef struct SYS_FTRUNCATE_PARAMS {
+ int fildes;
+ size_t length;
+} __attribute__((packed)) SYS_FTRUNCATE_PARAMS;
+
+typedef struct SYS_STAT_PARAMS {
+ const char *pathname;
+ struct stat *statbuf;
+} __attribute__((packed)) SYS_STAT_PARAMS;
+#endif
diff --git a/userland/libc/include/syslog.h b/userland/libc/include/syslog.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/syslog.h
diff --git a/userland/libc/include/termios.h b/userland/libc/include/termios.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/termios.h
diff --git a/userland/libc/include/time.h b/userland/libc/include/time.h
new file mode 100644
index 0000000..567e9ef
--- /dev/null
+++ b/userland/libc/include/time.h
@@ -0,0 +1,35 @@
+#ifndef TIME_H
+#define TIME_H
+#include <sys/types.h>
+#include <stddef.h>
+
+#define CLOCK_REALTIME 0
+
+struct tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ long __tm_gmtoff;
+ const char *__tm_zone;
+};
+
+typedef int clockid_t;
+struct timespec {
+ time_t tv_sec; // Seconds.
+ long tv_nsec; // Nanoseconds.
+};
+
+time_t time(time_t *tloc);
+int clock_gettime(clockid_t clock_id, struct timespec *tp);
+struct tm *localtime(const time_t *timer);
+struct tm *gmtime(const time_t *timer);
+size_t strftime(char *restrict s, size_t maxsize,
+ const char *restrict format, const struct tm *restrict timeptr);
+char *ctime_r(const time_t *clock, char *buf);
+#endif
diff --git a/userland/libc/include/ubsan.h b/userland/libc/include/ubsan.h
new file mode 100644
index 0000000..dac5407
--- /dev/null
+++ b/userland/libc/include/ubsan.h
@@ -0,0 +1,79 @@
+#include <stdint.h>
+
+enum { type_kind_int = 0, type_kind_float = 1, type_unknown = 0xffff };
+
+struct type_descriptor {
+ uint16_t type_kind;
+ uint16_t type_info;
+ char type_name[1];
+};
+
+struct source_location {
+ const char *file_name;
+ union {
+ unsigned long reported;
+ struct {
+ uint32_t line;
+ uint32_t column;
+ };
+ };
+};
+
+struct OverflowData {
+ struct source_location location;
+ struct type_descriptor *type;
+};
+
+struct type_mismatch_data {
+ struct source_location location;
+ struct type_descriptor *type;
+ unsigned long alignment;
+ unsigned char type_check_kind;
+};
+
+struct type_mismatch_data_v1 {
+ struct source_location location;
+ struct type_descriptor *type;
+ unsigned char log_alignment;
+ unsigned char type_check_kind;
+};
+
+struct type_mismatch_data_common {
+ struct source_location *location;
+ struct type_descriptor *type;
+ unsigned long alignment;
+ unsigned char type_check_kind;
+};
+
+struct nonnull_arg_data {
+ struct source_location location;
+ struct source_location attr_location;
+ int arg_index;
+};
+
+struct OutOfBoundsData {
+ struct source_location location;
+ struct type_descriptor *array_type;
+ struct type_descriptor *index_type;
+};
+
+struct ShiftOutOfBoundsData {
+ struct source_location location;
+ struct type_descriptor *lhs_type;
+ struct type_descriptor *rhs_type;
+};
+
+struct unreachable_data {
+ struct source_location location;
+};
+
+struct invalid_value_data {
+ struct source_location location;
+ struct type_descriptor *type;
+};
+
+struct alignment_assumption_data {
+ struct source_location location;
+ struct source_location assumption_location;
+ struct type_descriptor *type;
+};
diff --git a/userland/libc/include/unistd.h b/userland/libc/include/unistd.h
new file mode 100644
index 0000000..e43dc33
--- /dev/null
+++ b/userland/libc/include/unistd.h
@@ -0,0 +1,24 @@
+#ifndef UNISTD_H
+#define UNISTD_H
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define STDIN_FILENO 0
+
+extern int opterr, optind, optopt;
+extern char *optarg;
+
+int close(int fildes);
+int ftruncate(int fildes, uint64_t length);
+int execv(char *path, char **argv);
+int pipe(int fd[2]);
+int dup2(int org_fd, int new_fd);
+int getopt(int argc, char *const argv[], const char *optstring);
+pid_t getpid(void);
+int unlink(const char *path);
+int execvp(const char *file, char *const argv[]);
+void _exit(int status);
+void msleep(uint32_t ms); // not standard
+uint32_t uptime(void); // not standard
+#endif
diff --git a/userland/libc/include/utime.h b/userland/libc/include/utime.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/utime.h
diff --git a/userland/libc/include/wchar.h b/userland/libc/include/wchar.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/wchar.h
diff --git a/userland/libc/include/wctype.h b/userland/libc/include/wctype.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/include/wctype.h
diff --git a/userland/libc/input.h b/userland/libc/input.h
new file mode 100644
index 0000000..a6602f5
--- /dev/null
+++ b/userland/libc/input.h
@@ -0,0 +1,9 @@
+#ifndef INPUT_H
+#define INPUT_H
+#include <stdint.h>
+
+struct input_event {
+ uint16_t key;
+ uint8_t status;
+};
+#endif
diff --git a/userland/libc/inttypes.h b/userland/libc/inttypes.h
new file mode 100644
index 0000000..0bf39d1
--- /dev/null
+++ b/userland/libc/inttypes.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+// FIXME: These are not correct
+#define PRId64 "d"
+#define PRId32 "d"
diff --git a/userland/libc/isspace.c b/userland/libc/isspace.c
new file mode 100644
index 0000000..9ba2766
--- /dev/null
+++ b/userland/libc/isspace.c
@@ -0,0 +1,5 @@
+#include <ctype.h>
+
+int isspace(int c) {
+ return c == ' ' || (unsigned)c-'\t' < 5;
+}
diff --git a/userland/libc/libc.c b/userland/libc/libc.c
new file mode 100644
index 0000000..259ef0c
--- /dev/null
+++ b/userland/libc/libc.c
@@ -0,0 +1,287 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+int errno;
+
+char *errno_strings[] = {
+ "",
+ "Argument list too long.",
+ "Permission denied.",
+ "Address in use.",
+ "Address not available.",
+ "Address family not supported.",
+ "Resource unavailable, try again (may be the same value as [EWOULDBLOCK]).",
+ "Connection already in progress.",
+ "Bad file descriptor.",
+ "Bad message.",
+ "Device or resource busy.",
+ "Operation canceled.",
+ "No child processes.",
+ "Connection aborted.",
+ "Connection refused.",
+ "Connection reset.",
+ "Resource deadlock would occur.",
+ "Destination address required.",
+ "Mathematics argument out of domain of function.",
+ "Reserved.",
+ "File exists.",
+ "Bad address.",
+ "File too large.",
+ "Host is unreachable.",
+ "Identifier removed.",
+ "Illegal byte sequence.",
+ "Operation in progress.",
+ "Interrupted function.",
+ "Invalid argument.",
+ "I/O error.",
+ "Socket is connected.",
+ "Is a directory.",
+ "Too many levels of symbolic links.",
+ "File descriptor value too large.",
+ "Too many links.",
+ "Message too large.",
+ "Reserved.",
+ "Filename too long.",
+ "Network is down.",
+ "Connection aborted by network.",
+ "Network unreachable.",
+ "Too many files open in system.",
+ "No buffer space available.",
+ "No message is available on the STREAM head read "
+ "queue. [Option End]",
+ "No such device.",
+ "No such file or directory.",
+ "Executable file format error.",
+ "No locks available.",
+ "Reserved.",
+ "Not enough space.",
+ "No message of the desired type.",
+ "Protocol not available.",
+ "No space left on device.",
+ "No STREAM resources.",
+ "Not a STREAM.",
+ "Functionality not supported.",
+ "The socket is not connected.",
+ "Not a directory or a symbolic link to a directory.",
+ "Directory not empty.",
+ "State not recoverable.",
+ "Not a socket.",
+ "Not supported (may be the same value as [EOPNOTSUPP]).",
+ "Inappropriate I/O control operation.",
+ "No such device or address.",
+ "Operation not supported on socket (may be the same value as [ENOTSUP]).",
+ "Value too large to be stored in data type.",
+ "Previous owner died.",
+ "Operation not permitted.",
+ "Broken pipe.",
+ "Protocol error.",
+ "Protocol not supported.",
+ "Protocol wrong type for socket.",
+ "Result too large.",
+ "Read-only file system.",
+ "Invalid seek.",
+ "No such process.",
+ "Reserved.",
+ "Stream ioctl() timeout. [Option End]",
+ "Connection timed out.",
+ "Text file busy.",
+ "Operation would block (may be the same value as [EAGAIN]).",
+ "Cross-device link. ",
+};
+
+#define ASSERT_NOT_REACHED assert(0)
+
+#define TAB_SIZE 8
+
+// Functions preserve the registers ebx, esi, edi, ebp, and esp; while
+// eax, ecx, edx are scratch registers.
+
+// Syscall: eax ebx ecx edx esi edi
+int syscall(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx,
+ uint32_t esi, uint32_t edi) {
+ asm volatile("push %edi\n"
+ "push %esi\n"
+ "push %ebx\n"
+ "mov 0x1C(%ebp), %edi\n"
+ "mov 0x18(%ebp), %esi\n"
+ "mov 0x14(%ebp), %edx\n"
+ "mov 0x10(%ebp), %ecx\n"
+ "mov 0xc(%ebp), %ebx\n"
+ "mov 0x8(%ebp), %eax\n"
+ "int $0x80\n"
+ "pop %ebx\n"
+ "pop %esi\n"
+ "pop %edi\n");
+}
+
+int pipe(int fd[2]) { return syscall(SYS_PIPE, fd, 0, 0, 0, 0); }
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html
+char *strerror(int errnum) {
+ // The strerror() function shall map the error number in errnum to a
+ // locale-dependent error message string and shall return a pointer to it.
+ return errno_strings[errnum];
+}
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/perror.html
+void perror(const char *s) {
+ // The perror() function shall map the error number accessed through the
+ // symbol errno to a language-dependent error message, which shall be written
+ // to the standard error stream as follows:
+
+ // (First (if s is not a null pointer and the character pointed to
+ // by s is not the null byte),
+ if (s && *s != '\0') {
+ // the string pointed to by s
+ // followed by a <colon> and a <space>.
+ printf("%s: ", s);
+ }
+
+ // Then an error message string followed by a <newline>.
+ // The contents of the error message strings shall be the same as those
+ // returned by strerror() with argument errno.
+ printf("%s\n", strerror(errno));
+}
+
+int open(const char *file, int flags, int mode) {
+ struct SYS_OPEN_PARAMS args = {
+ .file = file,
+ .flags = flags,
+ .mode = mode,
+ };
+ RC_ERRNO(syscall(SYS_OPEN, &args, 0, 0, 0, 0));
+}
+
+int close(int fd) { return syscall(SYS_CLOSE, (void *)fd, 0, 0, 0, 0); }
+
+int execv(char *path, char **argv) {
+ struct SYS_EXEC_PARAMS args = {.path = path, .argv = argv};
+ return syscall(SYS_EXEC, &args, 0, 0, 0, 0);
+}
+/*
+int syscall(int sys, void *args) {
+ asm volatile("push %ebx\n"
+ "mov 0xc(%ebp), %ebx\n"
+ "mov 0x8(%ebp), %eax\n"
+ "int $0x80\n"
+ "pop %ebx\n");
+}*/
+
+int s_syscall(int sys) {
+ asm volatile("movl %0, %%eax\n"
+ "int $0x80\n" ::"r"((uint32_t)sys));
+}
+
+int write(int fd, const char *buf, size_t count) {
+ /*
+ struct SYS_WRITE_PARAMS args = {
+ .fd = fd,
+ .buf = buf,
+ .count = count,
+ };*/
+ return syscall(SYS_WRITE, fd, buf, count, 0, 0);
+}
+
+int pwrite(int fd, const char *buf, size_t count, size_t offset) {
+ struct SYS_PWRITE_PARAMS args = {
+ .fd = fd,
+ .buf = buf,
+ .count = count,
+ .offset = offset,
+ };
+ return syscall(SYS_PWRITE, &args, 0, 0, 0, 0);
+}
+
+int wait(int *stat_loc) { return syscall(SYS_WAIT, stat_loc, 0, 0, 0, 0); }
+
+void exit(int status) { syscall(SYS_EXIT, (void *)status, 0, 0, 0, 0); }
+
+int pread(int fd, void *buf, size_t count, size_t offset) {
+ struct SYS_PREAD_PARAMS args = {
+ .fd = fd,
+ .buf = buf,
+ .count = count,
+ .offset = offset,
+ };
+ RC_ERRNO(syscall(SYS_PREAD, &args, 0, 0, 0, 0));
+}
+
+int read(int fd, void *buf, size_t count) {
+ struct SYS_READ_PARAMS args = {
+ .fd = fd,
+ .buf = buf,
+ .count = count,
+ };
+ RC_ERRNO(syscall(SYS_READ, &args, 0, 0, 0, 0));
+}
+
+int dup2(int org_fd, int new_fd) {
+ struct SYS_DUP2_PARAMS args = {
+ .org_fd = org_fd,
+ .new_fd = new_fd,
+ };
+ RC_ERRNO(syscall(SYS_DUP2, &args, 0, 0, 0, 0));
+}
+
+int fork(void) { return s_syscall(SYS_FORK); }
+
+void dputc(int fd, const char c) { pwrite(fd, &c, 1, 0); }
+
+int brk(void *addr) { return syscall(SYS_BRK, addr, 0, 0, 0, 0); }
+
+void *sbrk(intptr_t increment) {
+ return (void *)syscall(SYS_SBRK, (void *)increment, 0, 0, 0, 0);
+}
+
+int poll(struct pollfd *fds, size_t nfds, int timeout) {
+ SYS_POLL_PARAMS args = {
+ .fds = fds,
+ .nfds = nfds,
+ .timeout = timeout,
+ };
+ RC_ERRNO(syscall(SYS_POLL, &args, 0, 0, 0, 0));
+}
+
+int socket(int domain, int type, int protocol) {
+ SYS_SOCKET_PARAMS args = {
+ .domain = domain,
+ .type = type,
+ .protocol = protocol,
+ };
+ RC_ERRNO(syscall(SYS_SOCKET, &args, 0, 0, 0, 0));
+}
+
+int accept(int socket, struct sockaddr *address, socklen_t *address_len) {
+ SYS_ACCEPT_PARAMS args = {
+ .socket = socket,
+ .address = address,
+ .address_len = address_len,
+ };
+ RC_ERRNO(syscall(SYS_ACCEPT, &args, 0, 0, 0, 0));
+}
+
+int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
+ SYS_BIND_PARAMS args = {
+ .sockfd = sockfd,
+ .addr = addr,
+ .addrlen = addrlen,
+ };
+ RC_ERRNO(syscall(SYS_BIND, &args, 0, 0, 0, 0));
+}
+
+int shm_open(const char *name, int oflag, mode_t mode) {
+ SYS_SHM_OPEN_PARAMS args = {
+ .name = name,
+ .oflag = oflag,
+ .mode = mode,
+ };
+ RC_ERRNO(syscall(SYS_SHM_OPEN, &args, 0, 0, 0, 0));
+}
+
+int ftruncate(int fildes, uint64_t length) {
+ SYS_FTRUNCATE_PARAMS args = {.fildes = fildes, .length = length};
+ RC_ERRNO(syscall(SYS_FTRUNCATE, &args, 0, 0, 0, 0));
+}
diff --git a/userland/libc/libgen/basename.c b/userland/libc/libgen/basename.c
new file mode 100644
index 0000000..a0a8adb
--- /dev/null
+++ b/userland/libc/libgen/basename.c
@@ -0,0 +1,45 @@
+#include <libgen.h>
+#include <stddef.h>
+
+/*
+The basename() function shall take the pathname pointed to by path and
+return a pointer to the final component of the pathname, deleting any
+trailing '/' characters.
+
+
+The basename() function may modify the string pointed to by path, and
+may return a pointer to internal storage. The returned pointer might be
+invalidated or the storage might be overwritten by a subsequent call to
+basename(). The returned pointer might also be invalidated if the
+calling thread is terminated.
+*/
+
+char *basename_empty_return_value = ".";
+char *basename_slash_return_value = "/";
+char *basename(char *path) {
+ // If path is a null pointer or points to an empty string, basename()
+ // shall return a pointer to the string ".".
+ if (NULL == path || '\0' == *path)
+ return basename_empty_return_value;
+
+ char *start = path;
+ // Move the string to the end
+ for (; *path; path++)
+ ;
+ if (start == path)
+ return start;
+ path--;
+ if ('/' == *path) // Trailing slash
+ *path = '\0';
+ // Loop until next slash is found
+ for (; path != start && '/' != *path; path--)
+ ;
+ // If the string pointed to by path consists entirely of the '/' character,
+ // basename() shall return a pointer to the string "/". If the string
+ // pointed to by path is exactly "//", it is implementation-defined whether
+ //'/' or "//" is returned.
+ path++;
+ if ('\0' == *path)
+ return basename_slash_return_value;
+ return path;
+}
diff --git a/userland/libc/libgen/dirname.c b/userland/libc/libgen/dirname.c
new file mode 100644
index 0000000..fb3c813
--- /dev/null
+++ b/userland/libc/libgen/dirname.c
@@ -0,0 +1,44 @@
+#include <libgen.h>
+
+char *dirname_empty_return_value = ".";
+char *dirname_slash_return_value = "/";
+char *dirname(char *path) {
+ // If path is a null pointer or points to an empty string,
+ // dirname() shall return a pointer to the string "."
+ if (!path)
+ return dirname_empty_return_value;
+ if ('\0' == *path)
+ return dirname_empty_return_value;
+
+ char *start = path;
+ for (; *path; path++)
+ ;
+ path--;
+ if ('/' == *path) {
+ if (start == path)
+ return path;
+ path--;
+ }
+
+ for (; path != start && '/' != *path; path--)
+ ;
+ // If path does not contain a '/', then dirname() shall return a pointer to
+ // the string ".".
+ if ('/' != *path)
+ return dirname_empty_return_value;
+
+ if (path == start)
+ return dirname_slash_return_value;
+
+ *path = '\0';
+ path--;
+
+ for (; path != start && '/' != *path; path--)
+ ;
+
+ if ('/' != *path)
+ return dirname_empty_return_value;
+
+ path++;
+ return path;
+}
diff --git a/userland/libc/limits.h b/userland/libc/limits.h
new file mode 100644
index 0000000..0c9389a
--- /dev/null
+++ b/userland/libc/limits.h
@@ -0,0 +1,2 @@
+#define PATH_MAX 256
+#define FILENAME_MAX PATH_MAX
diff --git a/userland/libc/malloc/malloc.c b/userland/libc/malloc/malloc.c
new file mode 100644
index 0000000..f351291
--- /dev/null
+++ b/userland/libc/malloc/malloc.c
@@ -0,0 +1,232 @@
+#include <assert.h>
+#include <errno.h>
+#include <malloc/malloc.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define NEW_ALLOC_SIZE 0x20000
+#define MALLOC_HEADER_MAGIC 0x1337F00D
+#define MALLOC_HEADER_PAD 0x11223344
+
+#define IS_FREE (1 << 0)
+#define IS_FINAL (1 << 1)
+
+typedef struct MallocHeader {
+ uint32_t magic;
+ uint32_t size;
+ uint8_t flags;
+ struct MallocHeader *n;
+} MallocHeader;
+
+size_t max(size_t a, size_t b) { return (a > b) ? a : b; }
+
+//;size_t min(size_t a, size_t b) { return (a < b) ? a : b; }
+
+uint64_t delta_page(uint64_t a) { return 0x1000 - (a % 0x1000); }
+
+MallocHeader *head = NULL;
+MallocHeader *final = NULL;
+uint32_t total_heap_size = 0;
+
+int init_heap(void) {
+ head = (MallocHeader *)sbrk(NEW_ALLOC_SIZE);
+ if ((void *)-1 == head) {
+ perror("sbrk");
+ exit(1);
+ }
+ total_heap_size += NEW_ALLOC_SIZE - sizeof(MallocHeader);
+ head->magic = MALLOC_HEADER_MAGIC;
+ head->size = NEW_ALLOC_SIZE - sizeof(MallocHeader);
+ head->flags = IS_FREE | IS_FINAL;
+ head->n = NULL;
+ final = head;
+ return 1;
+}
+
+int add_heap_memory(size_t min_desired) {
+ min_desired += sizeof(MallocHeader) + 0x1000;
+ size_t allocation_size = max(min_desired, NEW_ALLOC_SIZE);
+ allocation_size += delta_page(allocation_size);
+ void *p;
+ if ((void *)(-1) == (p = (void *)sbrk(allocation_size))) {
+ perror("sbrk");
+ return 0;
+ }
+ total_heap_size += allocation_size - sizeof(MallocHeader);
+ void *e = final;
+ e = (void *)((uint32_t)e + final->size);
+ if (p == e) {
+ final->size += allocation_size - sizeof(MallocHeader);
+ return 1;
+ }
+ MallocHeader *new_entry = p;
+ new_entry->magic = MALLOC_HEADER_MAGIC;
+ new_entry->size = allocation_size - sizeof(MallocHeader);
+ new_entry->flags = IS_FREE | IS_FINAL;
+ new_entry->n = NULL;
+ final->n = new_entry;
+ final = new_entry;
+ return 1;
+}
+
+MallocHeader *next_header(MallocHeader *a) { return a->n; }
+
+MallocHeader *next_close_header(MallocHeader *a) {
+ if (!a) {
+ printf("next close header fail\n");
+ for (;;)
+ ;
+ }
+ if (a->flags & IS_FINAL)
+ return NULL;
+ return next_header(a);
+}
+
+MallocHeader *find_free_entry(uint32_t s, int align) {
+ // A new header is required as well as the newly allocated chunk
+ if (!head)
+ init_heap();
+ MallocHeader *p = head;
+ for (; p; p = next_header(p)) {
+ if (!(p->flags & IS_FREE))
+ continue;
+ uint64_t required_size = s;
+ if (align) {
+ void *addy = p;
+ addy = (void *)((uint32_t)addy + sizeof(MallocHeader));
+ uint64_t d = delta_page((uint32_t)addy);
+ if (d < sizeof(MallocHeader) && d != 0)
+ continue;
+ required_size += d;
+ }
+ if (p->size < required_size)
+ continue;
+ return p;
+ }
+ return NULL;
+}
+
+void merge_headers(MallocHeader *b) {
+ if (!(b->flags & IS_FREE))
+ return;
+
+ MallocHeader *n = next_close_header(b);
+ if (!n)
+ return;
+
+ if (n > 0xf58c0820 - 0x8 && n < 0xf58c0820 + 0x8) {
+ printf("b: %x\n", b);
+ printf("b->n: %x\n", b->n);
+ asm("hlt");
+ assert(0);
+ }
+ if (!(n->flags & IS_FREE))
+ return;
+
+ // Remove the next header and just increase the newly freed header
+ b->size += n->size;
+ b->flags |= n->flags & IS_FINAL;
+ b->n = n->n;
+ if (n == final)
+ final = b;
+}
+
+extern int errno;
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+void *int_malloc(size_t s, int align) {
+ if (!head)
+ init_heap();
+ size_t n = s;
+ MallocHeader *free_entry = find_free_entry(s, align);
+ if (!free_entry) {
+ if (!add_heap_memory(s)) {
+ errno = ENOMEM;
+ printf("ENOMEM\n");
+ return NULL;
+ }
+ return int_malloc(s, align);
+ }
+
+ void *rc = (void *)((uint32_t)free_entry + sizeof(MallocHeader));
+
+ if (align) {
+ uint64_t d = delta_page((uint32_t)rc);
+ n = d;
+ n -= sizeof(MallocHeader);
+ }
+
+ // Create a new header
+ MallocHeader *new_entry =
+ (MallocHeader *)((uint32_t)free_entry + n + sizeof(MallocHeader));
+ new_entry->magic = MALLOC_HEADER_MAGIC;
+ new_entry->flags = free_entry->flags;
+ new_entry->n = free_entry->n;
+ new_entry->size = free_entry->size - n - sizeof(MallocHeader);
+ if (free_entry == final)
+ final = new_entry;
+ merge_headers(new_entry);
+
+ // Modify the free entry
+ free_entry->size = n;
+ free_entry->flags = 0;
+ free_entry->n = new_entry;
+
+ if (align && ((uint32_t)rc % 0x1000) != 0) {
+ void *c = int_malloc(s, 1);
+ free(rc);
+ rc = c;
+ return rc;
+ }
+ return rc;
+}
+
+void *malloc(size_t s) { return int_malloc(s + 1, 0); }
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+void *calloc(size_t nelem, size_t elsize) {
+ // The calloc() function shall allocate unused space for an array of
+ // nelem elements each of whose size in bytes is elsize.
+ size_t alloc_size = nelem * elsize;
+ void *rc = malloc(alloc_size);
+ // The space shall be initialized to all bits 0.
+ memset(rc, 0, alloc_size);
+ return rc;
+}
+
+size_t get_mem_size(void *ptr) {
+ if (!ptr)
+ return 0;
+ return ((MallocHeader *)(ptr - sizeof(MallocHeader)))->size;
+}
+
+void *realloc(void *ptr, size_t size) {
+ void *rc = malloc(size);
+ if (!rc)
+ return NULL;
+ size_t l = get_mem_size(ptr);
+ size_t to_copy = min(l, size);
+ memcpy(rc, ptr, to_copy);
+ free(ptr);
+ return rc;
+}
+
+void free(void *p) {
+ if (!p)
+ return;
+ // FIXME: This assumes that p is at the start of a allocated area.
+ // Is this a assumption that can be made?
+ MallocHeader *h = (MallocHeader *)((uint32_t)p - sizeof(MallocHeader));
+ if (MALLOC_HEADER_MAGIC != h->magic) {
+ printf("h->magic: %x\n", h->magic);
+ printf("&h->magic: %x\n", &(h->magic));
+ assert(0);
+ }
+ if (h->flags & IS_FREE)
+ return;
+
+ h->flags |= IS_FREE;
+ merge_headers(h);
+}
diff --git a/userland/libc/malloc/malloc.h b/userland/libc/malloc/malloc.h
new file mode 100644
index 0000000..082d8ad
--- /dev/null
+++ b/userland/libc/malloc/malloc.h
@@ -0,0 +1,9 @@
+#ifndef MALLOC_H
+#define MALLOC_H
+#include <stdint.h>
+#include <stddef.h>
+
+void *malloc(size_t s);
+void *calloc(size_t nelem, size_t elsize);
+void free(void *p);
+#endif
diff --git a/userland/libc/malloc/oldmalloc.c b/userland/libc/malloc/oldmalloc.c
new file mode 100644
index 0000000..042049d
--- /dev/null
+++ b/userland/libc/malloc/oldmalloc.c
@@ -0,0 +1,136 @@
+#include "stdio.h"
+#include "stdlib.h"
+#include "../errno.h"
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+#define NEW_ALLOC_SIZE 0x1000
+
+#define IS_FREE (1 << 0)
+#define IS_FINAL (1 << 1)
+
+typedef struct MallocHeader {
+ uint32_t size;
+ uint8_t flags;
+} MallocHeader;
+
+MallocHeader *head = NULL;
+MallocHeader *final = NULL;
+uint32_t total_heap_size = 0;
+
+int init_heap(void) {
+ head = sbrk(NEW_ALLOC_SIZE);
+ if ((void *)-1 == head) {
+ // perror("sbrk");
+ return 0;
+ }
+ total_heap_size += NEW_ALLOC_SIZE;
+ head->size = NEW_ALLOC_SIZE;
+ head->flags = IS_FREE | IS_FINAL;
+ final = head;
+ return 1;
+}
+
+int add_heap_memory(void) {
+ if ((void *)-1 == sbrk(NEW_ALLOC_SIZE)) {
+ // perror("sbrk");
+ return 0;
+ }
+ total_heap_size += NEW_ALLOC_SIZE;
+ final->size += NEW_ALLOC_SIZE;
+ return 1;
+}
+
+MallocHeader *next_header(MallocHeader *a) {
+ if (a->flags & IS_FINAL)
+ return NULL;
+ return ((void *)a) + a->size;
+}
+
+MallocHeader *find_free_entry(uint32_t s) {
+ // A new header is required as well as the newly allocated chunk
+ s += sizeof(MallocHeader);
+ if (!head)
+ init_heap();
+ MallocHeader *p = head;
+ for (; p; p = next_header(p)) {
+ if (!(p->flags & IS_FREE))
+ continue;
+ if (p->size < s)
+ continue;
+ return p;
+ }
+ return NULL;
+}
+
+void merge_headers(MallocHeader *b) {
+ if (!(b->flags & IS_FREE))
+ return;
+
+ MallocHeader *n = next_header(b);
+ if (!n)
+ return;
+
+ if (!(n->flags & IS_FREE))
+ return;
+
+ // Remove the next header and just increase the newly freed header
+ b->size += n->size;
+ b->flags |= n->flags & IS_FINAL;
+ if (b->flags & IS_FINAL)
+ final = b;
+}
+
+extern int errno;
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+void *malloc(size_t s) {
+ if(s == 0)
+ s = 1;
+
+ MallocHeader *free_entry = find_free_entry(s);
+ if (!free_entry) {
+ if (!add_heap_memory()) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return malloc(s);
+ }
+
+ // Create a new header
+ MallocHeader *new_entry = ((void *)free_entry) + s;
+ new_entry->flags |= IS_FREE;
+ new_entry->size = free_entry->size - s - sizeof(MallocHeader);
+ new_entry->flags |= free_entry->flags & IS_FINAL;
+ if (new_entry->flags & IS_FINAL)
+ final = new_entry;
+ merge_headers(new_entry);
+
+ // Modify the free entry
+ free_entry->size = s;
+ free_entry->flags = 0;
+
+ return ((void *)free_entry) + sizeof(MallocHeader);
+}
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+void *calloc(size_t nelem, size_t elsize) {
+ // The calloc() function shall allocate unused space for an array of
+ // nelem elements each of whose size in bytes is elsize.
+ size_t alloc_size = nelem*elsize;
+ void *rc = malloc(alloc_size);
+ // The space shall be initialized to all bits 0.
+ memset(rc, 0, alloc_size);
+ return rc;
+}
+
+void free(void *p) {
+ // FIXME: This assumes that p is at the start of a allocated area.
+ // Is this a assumption that can be made?
+ MallocHeader *h = p - sizeof(MallocHeader);
+ if (h->flags & IS_FREE)
+ return;
+
+ h->flags |= IS_FREE;
+ merge_headers(h);
+}
diff --git a/userland/libc/math.h b/userland/libc/math.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/math.h
diff --git a/userland/libc/memset.c b/userland/libc/memset.c
new file mode 100644
index 0000000..51910e9
--- /dev/null
+++ b/userland/libc/memset.c
@@ -0,0 +1,15 @@
+#include <stddef.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+void *memset(void *s, int c, size_t n) {
+ // The memset() function shall copy c (converted to an unsigned
+ // char) into each of the first n bytes of the object pointed to by
+ // s.
+
+ unsigned char *p = s;
+ for (; n > 0; n--, p++)
+ *p = (unsigned char)c;
+
+ // The memset() function shall return s
+ return s;
+}
diff --git a/userland/libc/mmap.c b/userland/libc/mmap.c
new file mode 100644
index 0000000..06b79bd
--- /dev/null
+++ b/userland/libc/mmap.c
@@ -0,0 +1,19 @@
+#include <syscall.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+extern int errno;
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ size_t offset) {
+ SYS_MMAP_PARAMS args = {
+ .addr = addr,
+ .length = length,
+ .prot = prot,
+ .flags = flags,
+ .fd = fd,
+ .offset = offset,
+ };
+// return (void*)syscall(SYS_MMAP, &args);
+ RC_ERRNO(syscall(SYS_MMAP, &args));
+}
diff --git a/userland/libc/poll.h b/userland/libc/poll.h
new file mode 100644
index 0000000..88e98b3
--- /dev/null
+++ b/userland/libc/poll.h
@@ -0,0 +1,16 @@
+#ifndef POLL_H
+#define POLL_H
+#include <stddef.h>
+
+#define POLLIN (1 << 0)
+#define POLLPRI (1 << 1)
+#define POLLOUT (1 << 2)
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+int poll(struct pollfd *fds, size_t nfds, int timeout);
+#endif
diff --git a/userland/libc/pty.c b/userland/libc/pty.c
new file mode 100644
index 0000000..b7ddf00
--- /dev/null
+++ b/userland/libc/pty.c
@@ -0,0 +1,15 @@
+#include "pty.h"
+#include "syscall.h"
+
+int openpty(int *amaster, int *aslave, char *name,
+ /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp) {
+ SYS_OPENPTY_PARAMS args = {
+ .amaster = amaster,
+ .aslave = aslave,
+ .name = name,
+ .termp = termp,
+ .winp = winp,
+ };
+ syscall(SYS_OPENPTY, &args, 0, 0, 0, 0);
+}
diff --git a/userland/libc/pty.h b/userland/libc/pty.h
new file mode 100644
index 0000000..b8ce978
--- /dev/null
+++ b/userland/libc/pty.h
@@ -0,0 +1,6 @@
+#ifndef PTY_H
+#define PTY_H
+int openpty(int *amaster, int *aslave, char *name,
+ /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp);
+#endif
diff --git a/userland/libc/setjmp/longjmp.s b/userland/libc/setjmp/longjmp.s
new file mode 100644
index 0000000..8188f06
--- /dev/null
+++ b/userland/libc/setjmp/longjmp.s
@@ -0,0 +1,16 @@
+.global _longjmp
+.global longjmp
+.type _longjmp,@function
+.type longjmp,@function
+_longjmp:
+longjmp:
+ mov 4(%esp),%edx
+ mov 8(%esp),%eax
+ cmp $1,%eax
+ adc $0, %al
+ mov (%edx),%ebx
+ mov 4(%edx),%esi
+ mov 8(%edx),%edi
+ mov 12(%edx),%ebp
+ mov 16(%edx),%esp
+ jmp *20(%edx)
diff --git a/userland/libc/setjmp/setjmp.s b/userland/libc/setjmp/setjmp.s
new file mode 100644
index 0000000..4d19cf8
--- /dev/null
+++ b/userland/libc/setjmp/setjmp.s
@@ -0,0 +1,23 @@
+.global ___setjmp
+.hidden ___setjmp
+.global __setjmp
+.global _setjmp
+.global setjmp
+.type __setjmp,@function
+.type _setjmp,@function
+.type setjmp,@function
+___setjmp:
+__setjmp:
+_setjmp:
+setjmp:
+ mov 4(%esp), %eax
+ mov %ebx, (%eax)
+ mov %esi, 4(%eax)
+ mov %edi, 8(%eax)
+ mov %ebp, 12(%eax)
+ lea 4(%esp), %ecx
+ mov %ecx, 16(%eax)
+ mov (%esp), %ecx
+ mov %ecx, 20(%eax)
+ xor %eax, %eax
+ ret
diff --git a/userland/libc/socket.h b/userland/libc/socket.h
new file mode 100644
index 0000000..5e86b45
--- /dev/null
+++ b/userland/libc/socket.h
@@ -0,0 +1,41 @@
+#include <stddef.h>
+#include <stdint.h>
+
+#define AF_UNIX 0
+#define AF_LOCAL AF_UNIX
+
+#define INADDR_ANY 0
+
+typedef struct {
+ int domain;
+ int type;
+ int protocol;
+
+ // UNIX socket
+ char *path;
+ int incoming_fd;
+} SOCKET;
+
+typedef struct {
+ char *path;
+ SOCKET *s;
+} OPEN_UNIX_SOCKET;
+
+typedef uint32_t in_addr_t;
+typedef uint16_t in_port_t;
+typedef unsigned int sa_family_t;
+typedef uint32_t socklen_t;
+
+struct sockaddr {
+ sa_family_t sa_family; /* Address family */
+ char *sa_data; /* Socket address */
+};
+
+struct sockaddr_un {
+ sa_family_t sun_family; /* Address family */
+ char *sun_path; /* Socket pathname */
+};
+
+int socket(int domain, int type, int protocol);
+int accept(int socket, struct sockaddr *address, socklen_t *address_len);
+int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
diff --git a/userland/libc/stdio.h b/userland/libc/stdio.h
new file mode 100644
index 0000000..38aaf22
--- /dev/null
+++ b/userland/libc/stdio.h
@@ -0,0 +1,95 @@
+#ifndef STDIO_H
+#define STDIO_H
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// FIXME: Most of these should probably not be here. But I am too lazy
+// to fix it right now. This is futures mees problem to deal wth.
+
+#define EOF (-1)
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+typedef struct __IO_FILE FILE;
+struct __IO_FILE {
+ size_t (*write)(FILE *, const unsigned char *, size_t);
+ size_t (*read)(FILE *, unsigned char *, size_t);
+ int (*seek)(FILE *, long, int);
+ long offset_in_file;
+ int buffered_char;
+ int has_buffered_char;
+ uint8_t is_eof;
+ uint8_t has_error;
+ uint64_t file_size;
+ void *cookie;
+};
+
+size_t write_fd(FILE *f, const unsigned char *s, size_t l);
+size_t read_fd(FILE *f, unsigned char *s, size_t l);
+int seek_fd(FILE *stream, long offset, int whence);
+
+typedef struct {
+ int fd;
+} FILE_FD_COOKIE;
+
+extern FILE __stdin_FILE;
+extern FILE __stdout_FILE;
+extern FILE __stderr_FILE;
+
+#define stdin (&__stdin_FILE)
+#define stdout (&__stdout_FILE)
+//#define stderr (&__stderr_FILE)
+#define stderr (&__stdout_FILE)
+
+typedef int mode_t;
+
+void perror(const char *s);
+
+int putchar(int c);
+int puts(const char *s);
+int brk(void *addr);
+void *sbrk(intptr_t increment);
+int write(int fd, const char *buf, size_t count);
+int pwrite(int fd, const char *buf, size_t count, size_t offset);
+int printf(const char *format, ...);
+int pread(int fd, void *buf, size_t count, size_t offset);
+int read(int fd, void *buf, size_t count);
+int fork(void);
+int memcmp(const void *s1, const void *s2, size_t n);
+int wait(int *stat_loc);
+void exit(int status);
+void *memcpy(void *dest, const void *src, uint32_t n);
+int shm_open(const char *name, int oflag, mode_t mode);
+int dprintf(int fd, const char *format, ...);
+int vdprintf(int fd, const char *format, va_list ap);
+int vprintf(const char *format, va_list ap);
+int snprintf(char *str, size_t size, const char *format, ...);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+int vfprintf(FILE *f, const char *fmt, va_list ap);
+int fgetc(FILE *stream);
+int getchar(void);
+#define getc(_a) fgetc(_a)
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+FILE *fopen(const char *pathname, const char *mode);
+int fclose(FILE *stream);
+int fseek(FILE *stream, long offset, int whence);
+int fprintf(FILE *f, const char *fmt, ...);
+long strtol(const char *nptr, char **endptr, int base);
+char *strchr(const char *s, int c);
+char *strcat(char *s1, const char *s2);
+char *fgets(char *s, int n, FILE *stream);
+FILE *tmpfile(void);
+int feof(FILE *stream);
+int fscanf(FILE *stream, const char *format, ...);
+int ungetc(int c, FILE *stream);
+long ftell(FILE *stream);
+int fputc(int c, FILE *stream);
+int remove(const char *path);
+int ferror(FILE *stream);
+int fputs(const char *s, FILE *stream);
+int fflush(FILE *stream);
+#endif
diff --git a/userland/libc/stdio/dprintf.c b/userland/libc/stdio/dprintf.c
new file mode 100644
index 0000000..2f2aadb
--- /dev/null
+++ b/userland/libc/stdio/dprintf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int dprintf(int fd, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vdprintf(fd, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/fclose.c b/userland/libc/stdio/fclose.c
new file mode 100644
index 0000000..02d93ae
--- /dev/null
+++ b/userland/libc/stdio/fclose.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html
+// FIXME: Do some actual error checking.
+int fclose(FILE *stream) {
+ free(stream->cookie);
+ free(stream);
+ return 0;
+}
diff --git a/userland/libc/stdio/feof.c b/userland/libc/stdio/feof.c
new file mode 100644
index 0000000..7f46301
--- /dev/null
+++ b/userland/libc/stdio/feof.c
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+int feof(FILE *stream) {
+ return stream->is_eof;
+}
diff --git a/userland/libc/stdio/ferror.c b/userland/libc/stdio/ferror.c
new file mode 100644
index 0000000..8cb46cf
--- /dev/null
+++ b/userland/libc/stdio/ferror.c
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+int ferror(FILE *stream) {
+ return stream->has_error;
+}
diff --git a/userland/libc/stdio/fflush.c b/userland/libc/stdio/fflush.c
new file mode 100644
index 0000000..7a37c79
--- /dev/null
+++ b/userland/libc/stdio/fflush.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html
+int fflush(FILE *stream) {
+ // FIXME: Implement
+ return 0;
+}
diff --git a/userland/libc/stdio/fgetc.c b/userland/libc/stdio/fgetc.c
new file mode 100644
index 0000000..c69211f
--- /dev/null
+++ b/userland/libc/stdio/fgetc.c
@@ -0,0 +1,20 @@
+#include <assert.h>
+#include <stdio.h>
+
+int fgetc(FILE *stream) {
+ if (stream->has_buffered_char) {
+ stream->has_buffered_char = 0;
+ return stream->buffered_char;
+ }
+ char c;
+ if (1 == fread(&c, 1, 1, stream))
+ return (int)c;
+ // FIXME: Should use feof and ferror
+ if (stream->has_error)
+ return EOF;
+ if (stream->is_eof)
+ return EOF;
+ // How did we get here?
+ assert(0);
+ return EOF;
+}
diff --git a/userland/libc/stdio/fgetpos.c b/userland/libc/stdio/fgetpos.c
new file mode 100644
index 0000000..7f34d6a
--- /dev/null
+++ b/userland/libc/stdio/fgetpos.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+// FIXME: Error handling
+int fgetpos(FILE *restrict stream, fpos_t *restrict pos) {
+ *pos = (fpos_t)stream->offset_in_file;
+ return 0;
+}
diff --git a/userland/libc/stdio/fgets.c b/userland/libc/stdio/fgets.c
new file mode 100644
index 0000000..8e21501
--- /dev/null
+++ b/userland/libc/stdio/fgets.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+char *fgets(char *s, int n, FILE *stream) {
+ for (int i = 0; i < n; i++) {
+ char c;
+ fread(&c, 1, 1, stream);
+ if (stream->has_error)
+ return NULL;
+ if (stream->is_eof)
+ return NULL;
+ s[i] = c;
+ if (c == '\n')
+ break;
+ }
+ return s;
+}
diff --git a/userland/libc/stdio/fileno.c b/userland/libc/stdio/fileno.c
new file mode 100644
index 0000000..246cc51
--- /dev/null
+++ b/userland/libc/stdio/fileno.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <errno.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fileno.html
+// The fileno() function shall return the integer file descriptor associated
+// with the stream pointed to by stream.
+int fileno(FILE *stream) {
+ if (-1 == stream->fd) {
+ errno = EBADF;
+ return -1;
+ }
+ return stream->fd;
+}
diff --git a/userland/libc/stdio/fopen.c b/userland/libc/stdio/fopen.c
new file mode 100644
index 0000000..a29c7ef
--- /dev/null
+++ b/userland/libc/stdio/fopen.c
@@ -0,0 +1,57 @@
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+
+// FIXME: All modes not implemented
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html
+FILE *fopen(const char *pathname, const char *mode) {
+ uint8_t read = 0;
+ uint8_t write = 0;
+ uint8_t append = 0;
+ // FIXME: Not parsed correctly
+ for (; *mode; mode++) {
+ // r or rb
+ // Open file for reading.
+ // w or wb
+ // Truncate to zero length or create file for writing.
+ // a or ab
+ // Append; open or create file for writing at
+ // end-of-file.
+ switch (*mode) {
+ case 'r':
+ read = 1;
+ break;
+ case 'w':
+ write = 1;
+ break;
+ case 'a':
+ append = 1;
+ break;
+ }
+ }
+ int flag = 0;
+ if (read)
+ flag |= O_READ;
+ if (write)
+ flag |= O_WRITE;
+
+ int fd = open(pathname, flag, 0);
+ if (-1 == fd)
+ return NULL;
+
+ struct stat s;
+ stat(pathname, &s);
+
+ FILE *r = malloc(sizeof(FILE));
+ r->read = read_fd;
+ r->write = write_fd;
+ r->seek = seek_fd;
+ r->has_error = 0;
+ r->is_eof = 0;
+ r->offset_in_file = 0;
+ r->file_size = s.st_size;
+ r->cookie = NULL;
+ r->fd = fd;
+ return r;
+}
diff --git a/userland/libc/stdio/fprintf.c b/userland/libc/stdio/fprintf.c
new file mode 100644
index 0000000..f983065
--- /dev/null
+++ b/userland/libc/stdio/fprintf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int fprintf(FILE *f, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int rc = vfprintf(f, fmt, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/fputc.c b/userland/libc/stdio/fputc.c
new file mode 100644
index 0000000..7c8fa7c
--- /dev/null
+++ b/userland/libc/stdio/fputc.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int fputc(int c, FILE *stream) {
+ if (fwrite(&c, 1, 1, stream) > 0)
+ return c;
+ return EOF;
+}
diff --git a/userland/libc/stdio/fputs.c b/userland/libc/stdio/fputs.c
new file mode 100644
index 0000000..1b70c66
--- /dev/null
+++ b/userland/libc/stdio/fputs.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int fputs(const char *s, FILE *stream) {
+ const char *b = s;
+ for (; *s; s++)
+ if (0 == fwrite(s, 1, 1, stream))
+ return EOF;
+ return s - b;
+}
diff --git a/userland/libc/stdio/fread.c b/userland/libc/stdio/fread.c
new file mode 100644
index 0000000..1a27afa
--- /dev/null
+++ b/userland/libc/stdio/fread.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <sys/types.h>
+
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ // FIXME: Check for overflow
+ ssize_t bytes_to_read = nmemb * size;
+ size_t rc = stream->read(stream, ptr, bytes_to_read);
+ // On success, fread() return the number of items read
+ rc /= size;
+ return rc;
+}
diff --git a/userland/libc/stdio/fscanf.c b/userland/libc/stdio/fscanf.c
new file mode 100644
index 0000000..785ce4b
--- /dev/null
+++ b/userland/libc/stdio/fscanf.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <assert.h>
+
+int fscanf(FILE *stream, const char *format, ...) {
+ // FIXME
+ assert(0);
+}
diff --git a/userland/libc/stdio/fseek.c b/userland/libc/stdio/fseek.c
new file mode 100644
index 0000000..fb891ec
--- /dev/null
+++ b/userland/libc/stdio/fseek.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <assert.h>
+
+int fseek(FILE *stream, long offset, int whence) {
+ return stream->seek(stream, offset, whence);
+ /*
+ switch (whence) {
+ case SEEK_SET:
+ stream->offset_in_file = offset;
+ break;
+ case SEEK_CUR:
+ stream->offset_in_file += offset;
+ break;
+ case SEEK_END:
+ // FIXME
+ assert(0);
+ break;
+ }
+ // FIXME: Error checking
+ return 0;*/
+}
diff --git a/userland/libc/stdio/fsetpos.c b/userland/libc/stdio/fsetpos.c
new file mode 100644
index 0000000..c39c545
--- /dev/null
+++ b/userland/libc/stdio/fsetpos.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+// FIXME: Error handling
+int fsetpos(FILE *stream, const fpos_t *pos) {
+ stream->offset_in_file = (long)(*pos);
+ return 0;
+}
diff --git a/userland/libc/stdio/ftell.c b/userland/libc/stdio/ftell.c
new file mode 100644
index 0000000..35076d0
--- /dev/null
+++ b/userland/libc/stdio/ftell.c
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+long ftell(FILE *stream) {
+ return stream->offset_in_file;
+}
diff --git a/userland/libc/stdio/fwrite.c b/userland/libc/stdio/fwrite.c
new file mode 100644
index 0000000..552bbd6
--- /dev/null
+++ b/userland/libc/stdio/fwrite.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <sys/types.h>
+
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ // FIXME: Check for overflow
+ ssize_t bytes_to_write = nmemb * size;
+ size_t rc = stream->write(stream, ptr, bytes_to_write);
+ // On success, fwrite() return the number of items
+ // written.
+ rc /= size;
+ return rc;
+}
diff --git a/userland/libc/stdio/getchar.c b/userland/libc/stdio/getchar.c
new file mode 100644
index 0000000..dad2263
--- /dev/null
+++ b/userland/libc/stdio/getchar.c
@@ -0,0 +1,4 @@
+#include <stdio.h>
+
+// The getchar() function shall be equivalent to getc(stdin).
+int getchar(void) { return fgetc(stdin); }
diff --git a/userland/libc/stdio/open_memstream.c b/userland/libc/stdio/open_memstream.c
new file mode 100644
index 0000000..8f359b9
--- /dev/null
+++ b/userland/libc/stdio/open_memstream.c
@@ -0,0 +1,108 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+/*
+struct __IO_FILE {
+ size_t (*write)(FILE *, const unsigned char *, size_t);
+ size_t (*read)(FILE *, unsigned char *, size_t);
+ int (*seek)(FILE *, long, int);
+ long offset_in_file;
+ int buffered_char;
+ int has_buffered_char;
+ int fd;
+ uint8_t is_eof;
+ uint8_t has_error;
+ uint64_t file_size;
+ void *cookie;
+};
+*/
+
+struct Memstream {
+ size_t buffer_usage;
+ char *buffer;
+};
+
+#define MEMSTREAM_DEF_SIZE 4096
+
+size_t memstream_write(FILE *fp, const unsigned char *buf, size_t n) {
+ struct Memstream *c = fp->cookie;
+ // FIXME: Do a reallocation
+ if (c->buffer_usage + n >= fp->file_size) {
+ n = fp->file_size - c->buffer_usage;
+ }
+
+ memcpy(c->buffer + fp->offset_in_file, buf, n);
+ fp->offset_in_file += n;
+ if (fp->offset_in_file > c->buffer_usage)
+ c->buffer_usage = fp->offset_in_file;
+ return n;
+}
+
+size_t memstream_read(FILE *fp, unsigned char *buf, size_t n) {
+ struct Memstream *c = fp->cookie;
+ size_t length_left = c->buffer_usage - fp->offset_in_file;
+ n = min(length_left, n);
+ memcpy(buf, c->buffer + fp->offset_in_file, n);
+ fp->offset_in_file += n;
+ if (0 == n)
+ fp->is_eof = 1;
+ return n;
+}
+
+int memstream_seek(FILE *stream, long offset, int whence) {
+ switch (whence) {
+ case SEEK_SET:
+ stream->offset_in_file = offset;
+ break;
+ case SEEK_CUR:
+ stream->offset_in_file += offset;
+ break;
+ case SEEK_END:
+ stream->offset_in_file = stream->file_size + offset;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ // FIXME: Error checking
+ return 0;
+}
+
+FILE *open_memstream(char **bufp, size_t *sizep) {
+ struct Memstream *c = NULL;
+ FILE *fp = malloc(sizeof(FILE));
+ if (!fp)
+ return NULL;
+
+ fp->offset_in_file = 0;
+ fp->buffered_char = 0;
+ fp->has_buffered_char = 0;
+ fp->seek = memstream_seek;
+ fp->fd = -1;
+ fp->is_eof = 0;
+ fp->has_error = 0;
+ fp->file_size = MEMSTREAM_DEF_SIZE;
+
+ fp->write = memstream_write;
+ fp->read = memstream_read;
+
+ c = malloc(sizeof(struct Memstream));
+ if (!c) {
+ goto _exit_memstream_fail;
+ }
+
+ fp->cookie = (void *)c;
+
+ c->buffer = *bufp = malloc(MEMSTREAM_DEF_SIZE);
+ if (!bufp) {
+ goto _exit_memstream_fail;
+ }
+ c->buffer_usage = 0;
+
+ return fp;
+_exit_memstream_fail:
+ free(c);
+ free(fp);
+ return NULL;
+}
diff --git a/userland/libc/stdio/printf.c b/userland/libc/stdio/printf.c
new file mode 100644
index 0000000..d26568a
--- /dev/null
+++ b/userland/libc/stdio/printf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int printf(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vprintf(format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/putc.c b/userland/libc/stdio/putc.c
new file mode 100644
index 0000000..a468a95
--- /dev/null
+++ b/userland/libc/stdio/putc.c
@@ -0,0 +1,3 @@
+#include <stdio.h>
+
+int putc(int c, FILE *stream) { return fputc(c, stream);}
diff --git a/userland/libc/stdio/putchar.c b/userland/libc/stdio/putchar.c
new file mode 100644
index 0000000..3fcf7ca
--- /dev/null
+++ b/userland/libc/stdio/putchar.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int putchar(int c) {
+ printf("%c", (char)c);
+ return c;
+}
diff --git a/userland/libc/stdio/puts.c b/userland/libc/stdio/puts.c
new file mode 100644
index 0000000..4a72e66
--- /dev/null
+++ b/userland/libc/stdio/puts.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int puts(const char *s) {
+ int rc = printf("%s\n", s);
+ return rc;
+}
diff --git a/userland/libc/stdio/remove.c b/userland/libc/stdio/remove.c
new file mode 100644
index 0000000..35b41ad
--- /dev/null
+++ b/userland/libc/stdio/remove.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <errno.h>
+
+extern int errno;
+int remove(const char *path) {
+ // FIXME
+ errno = ENAMETOOLONG;
+ return -1;
+}
diff --git a/userland/libc/stdio/rename.c b/userland/libc/stdio/rename.c
new file mode 100644
index 0000000..15d4bf5
--- /dev/null
+++ b/userland/libc/stdio/rename.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+#include <assert.h>
+
+int rename(const char *old, const char *new) {
+ (void)old;
+ (void)new;
+ assert(0); // TODO: Implement
+ }
diff --git a/userland/libc/stdio/setvbuf.c b/userland/libc/stdio/setvbuf.c
new file mode 100644
index 0000000..7f91518
--- /dev/null
+++ b/userland/libc/stdio/setvbuf.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size) {
+ // TODO
+ return 0;
+}
diff --git a/userland/libc/stdio/snprintf.c b/userland/libc/stdio/snprintf.c
new file mode 100644
index 0000000..328442a
--- /dev/null
+++ b/userland/libc/stdio/snprintf.c
@@ -0,0 +1,42 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+struct sn_cookie {
+ char *s;
+ size_t n;
+};
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+size_t sn_write(FILE *f, const unsigned char *s, const size_t l) {
+ struct sn_cookie *c = f->cookie;
+ size_t k = MIN(l, c->n);
+ memcpy(c->s, s, k);
+ c->s += k;
+ c->n -= k;
+ *(c->s) = '\0';
+ // Upon successful completion, the snprintf() function shall return the number
+ // of bytes that would be written to s had n been sufficiently large excluding
+ // the terminating null byte.
+ return l;
+}
+
+int vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ char dummy[1];
+ struct sn_cookie c = {.s = (size ? str : dummy), .n = (size ? size - 1 : 0)};
+ FILE f = {
+ .write = sn_write,
+ .cookie = &c,
+ };
+ return vfprintf(&f, format, ap);
+}
+
+int snprintf(char *str, size_t size, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/sprintf.c b/userland/libc/stdio/sprintf.c
new file mode 100644
index 0000000..deffbbe
--- /dev/null
+++ b/userland/libc/stdio/sprintf.c
@@ -0,0 +1,33 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+struct s_cookie {
+ char *s;
+};
+
+size_t s_write(FILE *f, const unsigned char *s, size_t l) {
+ struct s_cookie *c = f->cookie;
+ memcpy(c->s, s, l);
+ c->s += l;
+ *(c->s) = '\0';
+ return l;
+}
+
+int vsprintf(char *str, const char *format, va_list ap) {
+ struct s_cookie c = {.s = str};
+ FILE f = {
+ .write = s_write,
+ .cookie = &c,
+ };
+ return vfprintf(&f, format, ap);
+}
+
+int sprintf(char *str, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vsprintf(str, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/stderr.c b/userland/libc/stdio/stderr.c
new file mode 100644
index 0000000..76597e2
--- /dev/null
+++ b/userland/libc/stdio/stderr.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <unistd.h>
+
+FILE __stderr_FILE = {
+ .write = write_fd,
+ .read = read_fd,
+ .is_eof = 0,
+ .has_error = 0,
+ .cookie = NULL,
+ .fd = 2,
+};
diff --git a/userland/libc/stdio/stdin.c b/userland/libc/stdio/stdin.c
new file mode 100644
index 0000000..ae3ab8d
--- /dev/null
+++ b/userland/libc/stdio/stdin.c
@@ -0,0 +1,54 @@
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+size_t write_fd(FILE *f, const unsigned char *s, size_t l) {
+ int rc = pwrite(f->fd, s, l, f->offset_in_file);
+ if (rc == -1) {
+ f->has_error = 1;
+ return 0;
+ }
+ f->offset_in_file += rc;
+ return rc;
+}
+
+size_t read_fd(FILE *f, unsigned char *s, size_t l) {
+ int rc = pread(f->fd, s, l, f->offset_in_file);
+ if (rc == 0)
+ f->is_eof = 1;
+ if (rc == -1) {
+ f->has_error = 1;
+ return 0;
+ }
+ f->offset_in_file += rc;
+ return rc;
+}
+
+int seek_fd(FILE *stream, long offset, int whence) {
+ switch (whence) {
+ case SEEK_SET:
+ stream->offset_in_file = offset;
+ break;
+ case SEEK_CUR:
+ stream->offset_in_file += offset;
+ break;
+ case SEEK_END:
+ stream->offset_in_file = stream->file_size + offset;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ // FIXME: Error checking
+ return 0;
+}
+
+FILE __stdin_FILE = {
+ .write = write_fd,
+ .read = read_fd,
+ .seek = NULL,
+ .is_eof = 0,
+ .has_error = 0,
+ .cookie = NULL,
+ .fd = 0,
+};
diff --git a/userland/libc/stdio/stdout.c b/userland/libc/stdio/stdout.c
new file mode 100644
index 0000000..7f4edf0
--- /dev/null
+++ b/userland/libc/stdio/stdout.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <unistd.h>
+
+FILE __stdout_FILE = {
+ .write = write_fd,
+ .read = read_fd,
+ .is_eof = 0,
+ .has_error = 0,
+ .seek = NULL,
+ .cookie = NULL,
+ .fd = 1,
+};
+FILE __stderr_FILE;
diff --git a/userland/libc/stdio/tmpfile.c b/userland/libc/stdio/tmpfile.c
new file mode 100644
index 0000000..cee6e0a
--- /dev/null
+++ b/userland/libc/stdio/tmpfile.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <assert.h>
+
+FILE *tmpfile(void) {
+ // TODO
+ printf("TODO: Implement tmpfile()\n");
+ assert(0);
+ return NULL;
+}
diff --git a/userland/libc/stdio/tmpnam.c b/userland/libc/stdio/tmpnam.c
new file mode 100644
index 0000000..aafe67d
--- /dev/null
+++ b/userland/libc/stdio/tmpnam.c
@@ -0,0 +1,10 @@
+#include <assert.h>
+#include <stdio.h>
+
+char *tmpnam(char *s) {
+ assert(!s);
+ s = malloc(100);
+ strcpy(s, "/tmp.XXXXXX");
+ mkstemp(s);
+ return s;
+}
diff --git a/userland/libc/stdio/ungetc.c b/userland/libc/stdio/ungetc.c
new file mode 100644
index 0000000..8d649bc
--- /dev/null
+++ b/userland/libc/stdio/ungetc.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int ungetc(int c, FILE *stream) {
+ if (stream->has_buffered_char)
+ return EOF;
+ stream->buffered_char = c;
+ stream->has_buffered_char = 1;
+ return c;
+}
diff --git a/userland/libc/stdio/vdprintf.c b/userland/libc/stdio/vdprintf.c
new file mode 100644
index 0000000..b3fa065
--- /dev/null
+++ b/userland/libc/stdio/vdprintf.c
@@ -0,0 +1,77 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+struct vd_cookie {
+ int fd;
+ char *buffer;
+ uint8_t buf_len;
+ uint8_t buf_used;
+ int sent_bytes;
+};
+
+size_t min(size_t a, size_t b) { return (a < b) ? a : b; }
+
+size_t vd_write(FILE *f, const unsigned char *s, size_t l) {
+ struct vd_cookie *c = f->cookie;
+
+ int clear_buffer = 0;
+ size_t b_copy = min(l, c->buf_len - (c->buf_used));
+ for (int i = 0; i < b_copy; i++) {
+ c->buffer[c->buf_used + i] = s[i];
+ if (s[i] == '\n')
+ clear_buffer = 1;
+ }
+ c->buf_used += b_copy;
+
+ if (clear_buffer) {
+ int rc = write(c->fd, c->buffer, c->buf_used);
+ c->buf_used = 0;
+ if (-1 == rc) {
+ return (size_t)-1;
+ }
+ c->sent_bytes += rc;
+ }
+ return l;
+}
+
+int vdprintf(int fd, const char *format, va_list ap) {
+ FILE f = {
+ .write = write_fd,
+ .fd = fd,
+ };
+ return vfprintf(&f, format, ap);
+ // return -1;
+ /*
+char buffer[32];
+struct vd_cookie c = {.fd = fd,
+ .buffer = buffer,
+ .buf_len = 32,
+ .buf_used = 0,
+ .sent_bytes = 0};
+FILE f = {
+.write = vd_write,
+.cookie = &c,
+};
+
+// If an output error was encountered, these functions shall return a
+// negative value and set errno to indicate the error.
+if (-1 == vfprintf(&f, format, ap))
+return -1;
+
+// Upon successful completion, the dprintf(),
+// fprintf(), and printf() functions shall return the number of bytes
+// transmitted.
+
+if(0 == c.buf_used)
+return c.sent_bytes;
+
+// First the current buffer needs to be cleared
+int rc = write(fd, buffer, c.buf_used);
+if (-1 == rc) {
+return -1;
+}
+c.sent_bytes += rc;
+return c.sent_bytes;*/
+}
diff --git a/userland/libc/stdio/vfprintf.c b/userland/libc/stdio/vfprintf.c
new file mode 100644
index 0000000..79a22fb
--- /dev/null
+++ b/userland/libc/stdio/vfprintf.c
@@ -0,0 +1,243 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+const char HEX_SET[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+#define FILE_WRITE(_f, _s, _l, _r) \
+ { \
+ size_t _rc = _f->write(_f, (const unsigned char *)_s, _l); \
+ if ((size_t)-1 == _rc) \
+ assert(0); \
+ *(int *)(_r) += _rc; \
+ }
+// if ((size_t)0 == _rc) \
+// assert(0); \
+
+int fprint_num(FILE *f, int n, int base, char *char_set, int prefix,
+ int zero_padding, int right_padding) {
+ int c = 0;
+ if (0 == n) {
+ zero_padding = 1;
+ prefix = 1;
+ }
+ char str[32];
+ int i = 0;
+ for (; n != 0 && i < 32; i++, n /= base)
+ // str[i] = (n % base) + '0';
+ str[i] = char_set[(n % base)];
+
+ char t = (zero_padding) ? '0' : ' ';
+ int orig_i = i;
+
+ if (!right_padding) {
+ for (; prefix - orig_i > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+
+ for (i--; i >= 0; i--)
+ FILE_WRITE(f, &(str[i]), 1, &c);
+
+ if (right_padding) {
+ for (; prefix - orig_i > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+ return c;
+}
+
+int fprint_int(FILE *f, int n, int prefix, int zero_padding,
+ int right_padding) {
+ return fprint_num(f, n, 10, "0123456789", prefix, zero_padding,
+ right_padding);
+}
+
+int fprint_hex(FILE *f, int n, int prefix, int zero_padding,
+ int right_padding) {
+ return fprint_num(f, n, 16, "0123456789abcdef", prefix, zero_padding,
+ right_padding);
+}
+
+int fprint_octal(FILE *f, int n, int prefix, int zero_padding,
+ int right_padding) {
+ return fprint_num(f, n, 8, "012345678", prefix, zero_padding, right_padding);
+}
+
+int print_string(FILE *f, const char *s, int *rc, int prefix, int right_padding,
+ int precision) {
+ int l = strlen(s);
+ char t = ' ';
+ int c = 0;
+ if (!right_padding) {
+ if (prefix)
+ assert(-1 == precision); // FIXME: Is this correct?
+ for (; prefix - l > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+ int bl = precision;
+ for (; *s; s++, (*rc)++) {
+ if (precision != -1) {
+ if (0 == bl)
+ break;
+ bl--;
+ }
+ int r;
+ FILE_WRITE(f, (const unsigned char *)s, 1, &r);
+ assert(r != 0);
+ }
+ if (right_padding) {
+ assert(-1 == precision); // FIXME: Is this correct?
+ for (; prefix - l > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+ (*rc) += c;
+ return 0;
+}
+
+int parse_precision(const char **fmt) {
+ const char *s = *fmt;
+ int rc = 0;
+ for (int i = 0;; i++, s++) {
+ if ('\0' == *s)
+ break;
+ const char c = *s;
+ if ('*' == c) {
+ assert(i == 0);
+ return -1;
+ } else if (!(c >= '0' && c <= '9')) {
+ s--;
+ break;
+ }
+ rc *= 10;
+ rc += c - '0';
+ }
+ *fmt = s;
+ return rc;
+}
+
+int vfprintf(FILE *f, const char *fmt, va_list ap) {
+ int rc = 0;
+ const char *s = fmt;
+ int prefix = 0;
+
+ int zero_padding = 0;
+ int right_padding = 0;
+
+ int cont = 0;
+ int precision = -1;
+ for (; *s; s++) {
+ if (!cont && '%' != *s) {
+ FILE_WRITE(f, (const unsigned char *)s, 1, &rc);
+ continue;
+ }
+ if (!cont) {
+ cont = 1;
+ continue;
+ }
+
+ if ('\0' == *s)
+ break;
+
+ switch (*s) {
+ case '.':
+ s++;
+ assert('\0' != *s);
+ precision = parse_precision(&s);
+ assert('\0' != *s);
+ if (-1 == precision)
+ precision = va_arg(ap, int);
+ cont = 1;
+ break;
+ case '0':
+ prefix *= 10;
+ if (0 == prefix)
+ zero_padding = 1;
+ cont = 1;
+ break;
+ case '-':
+ assert(0 == prefix);
+ right_padding = 1;
+ cont = 1;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ prefix *= 10;
+ prefix += (*s) - '0';
+ cont = 1;
+ break;
+ case 'i':
+ case 'd':
+ if(-1 != precision) {
+ zero_padding = 1;
+ prefix = precision;
+ right_padding = 0;
+ }
+ rc += fprint_int(f, va_arg(ap, int), prefix, zero_padding, right_padding);
+ cont = 0;
+ break;
+ case 'u':
+ assert(-1 == precision);
+ rc += fprint_int(f, va_arg(ap, unsigned int), prefix, zero_padding,
+ right_padding);
+ cont = 0;
+ break;
+ case 's': {
+ assert(!zero_padding); // this is not supported to strings
+ char *a = va_arg(ap, char *);
+ if (!a) {
+ if (-1 ==
+ print_string(f, "(NULL)", &rc, prefix, right_padding, precision))
+ return -1;
+ cont = 0;
+ break;
+ }
+ if (-1 == print_string(f, a, &rc, prefix, right_padding, precision))
+ return -1;
+ cont = 0;
+ break;
+ }
+ case 'p': // TODO: Print this out in a nicer way
+ case 'x':
+ assert(-1 == precision);
+ rc += fprint_hex(f, va_arg(ap, const uint32_t), prefix, zero_padding,
+ right_padding);
+ cont = 0;
+ break;
+ case 'o':
+ assert(-1 == precision);
+ rc += fprint_octal(f, va_arg(ap, const uint32_t), prefix, zero_padding,
+ right_padding);
+ cont = 0;
+ break;
+ case '%': {
+ FILE_WRITE(f, (const unsigned char *)"%", 1, &rc);
+ cont = 0;
+ break;
+ }
+ case 'c': {
+ char c = va_arg(ap, const int);
+ FILE_WRITE(f, (const unsigned char *)&c, 1, &rc);
+ cont = 0;
+ break;
+ }
+ default:
+ printf("got %c but that is not supported by printf\n", *s);
+ assert(0);
+ break;
+ }
+ if (!cont) {
+ prefix = 0;
+ zero_padding = right_padding = 0;
+ precision = -1;
+ }
+ }
+ return rc;
+}
diff --git a/userland/libc/stdio/vprintf.c b/userland/libc/stdio/vprintf.c
new file mode 100644
index 0000000..8a8dc33
--- /dev/null
+++ b/userland/libc/stdio/vprintf.c
@@ -0,0 +1,3 @@
+#include <stdio.h>
+
+int vprintf(const char *format, va_list ap) { return vdprintf(1, format, ap); }
diff --git a/userland/libc/stdlib.h b/userland/libc/stdlib.h
new file mode 100644
index 0000000..bba5d84
--- /dev/null
+++ b/userland/libc/stdlib.h
@@ -0,0 +1,17 @@
+#ifndef STDLIB_H
+#define STDLIB_H
+#include <stddef.h>
+#include <limits.h>
+#define RAND_MAX (UINT32_MAX)
+
+void *malloc(size_t s);
+void *calloc(size_t nelem, size_t elsize);
+void *realloc(void *ptr, size_t size);
+void free(void *p);
+char *getenv(const char *name);
+int rand(void);
+void srand(unsigned int seed);
+unsigned long strtoul(const char *restrict str,
+ char **restrict endptr, int base);
+int atoi(const char *str);
+#endif
diff --git a/userland/libc/stdlib/abort.c b/userland/libc/stdlib/abort.c
new file mode 100644
index 0000000..7fd747e
--- /dev/null
+++ b/userland/libc/stdlib/abort.c
@@ -0,0 +1,10 @@
+#include <assert.h>
+#include <stdlib.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/abort.html
+void abort(void) {
+ printf("aborting!!!!\n");
+ assert(0);
+ for (;;)
+ ;
+}
diff --git a/userland/libc/stdlib/abs.c b/userland/libc/stdlib/abs.c
new file mode 100644
index 0000000..1079beb
--- /dev/null
+++ b/userland/libc/stdlib/abs.c
@@ -0,0 +1,3 @@
+#include <stdlib.h>
+
+int abs(int i) { return (i < 0) ? (-i) : (i); }
diff --git a/userland/libc/stdlib/atexit.c b/userland/libc/stdlib/atexit.c
new file mode 100644
index 0000000..0e401ff
--- /dev/null
+++ b/userland/libc/stdlib/atexit.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+int atexit(void (*func)(void)) {
+ //TODO
+ return 0;
+}
diff --git a/userland/libc/stdlib/atof.c b/userland/libc/stdlib/atof.c
new file mode 100644
index 0000000..8524f8b
--- /dev/null
+++ b/userland/libc/stdlib/atof.c
@@ -0,0 +1,5 @@
+#include <stdlib.h>
+
+double atof(const char *str) {
+ return strtod(str,(char **)NULL);
+}
diff --git a/userland/libc/stdlib/atoi.c b/userland/libc/stdlib/atoi.c
new file mode 100644
index 0000000..2183306
--- /dev/null
+++ b/userland/libc/stdlib/atoi.c
@@ -0,0 +1,4 @@
+#include <stdlib.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+int atoi(const char *str) { return (int)strtol(str, (char **)NULL, 10); }
diff --git a/userland/libc/stdlib/getenv.c b/userland/libc/stdlib/getenv.c
new file mode 100644
index 0000000..9a6a4e5
--- /dev/null
+++ b/userland/libc/stdlib/getenv.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+char *getenv(const char *name) {
+ // FIXME
+ return NULL;
+}
diff --git a/userland/libc/stdlib/mkstemp.c b/userland/libc/stdlib/mkstemp.c
new file mode 100644
index 0000000..1ea8790
--- /dev/null
+++ b/userland/libc/stdlib/mkstemp.c
@@ -0,0 +1,14 @@
+#include <fcntl.h>
+#include <stdlib.h>
+
+char rand_char(void) { return 'A' + (rand() % 10); }
+
+int mkstemp(char *template) {
+ // FIXME: Incomplete
+ const char *s = template;
+ for (; *template; template ++) {
+ if ('X' == *template)
+ *template = rand_char();
+ }
+ return open(s, O_RDWR, O_CREAT);
+}
diff --git a/userland/libc/stdlib/qsort.c b/userland/libc/stdlib/qsort.c
new file mode 100644
index 0000000..3f87db5
--- /dev/null
+++ b/userland/libc/stdlib/qsort.c
@@ -0,0 +1,29 @@
+#include <stdlib.h>
+#include <string.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/qsort.html
+void qsort(void *base, size_t nel, size_t width,
+ int (*compar)(const void *, const void *)) {
+ // If the nel argument has the value zero, the comparison function pointed to
+ // by compar shall not be called and no rearrangement shall take place.
+ if (0 == nel)
+ return;
+
+ // AB
+ // Results in negative
+ // BA
+ // Results in positive
+
+ // Using bubblesort
+ unsigned char *p = base;
+ for (size_t i = 1; i < nel; i++) {
+ for (size_t j = 0; j < nel; j++) {
+ if (compar((p + i * width), (p + j * width)) < 0) {
+ unsigned char tmp[width];
+ memcpy(tmp, (p + i * width), width);
+ memcpy((p + i * width), (p + j * width), width);
+ memcpy((p + j * width), tmp, width);
+ }
+ }
+ }
+}
diff --git a/userland/libc/stdlib/rand.c b/userland/libc/stdlib/rand.c
new file mode 100644
index 0000000..e186af7
--- /dev/null
+++ b/userland/libc/stdlib/rand.c
@@ -0,0 +1,17 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+uint32_t xorshift(uint32_t x) {
+ uint32_t f = x;
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return f + x;
+}
+
+extern uint32_t __INTERNAL_RNG_STATE;
+int rand(void) {
+ uint32_t x = xorshift(__INTERNAL_RNG_STATE);
+ __INTERNAL_RNG_STATE++;
+ return x;
+}
diff --git a/userland/libc/stdlib/srand.c b/userland/libc/stdlib/srand.c
new file mode 100644
index 0000000..a35185a
--- /dev/null
+++ b/userland/libc/stdlib/srand.c
@@ -0,0 +1,8 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+uint32_t __INTERNAL_RNG_STATE;
+void srand(unsigned int seed) {
+ __INTERNAL_RNG_STATE = seed;
+ __INTERNAL_RNG_STATE = rand(); // rand() used the internal rng state
+}
diff --git a/userland/libc/stdlib/strtod.c b/userland/libc/stdlib/strtod.c
new file mode 100644
index 0000000..2c83879
--- /dev/null
+++ b/userland/libc/stdlib/strtod.c
@@ -0,0 +1,70 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+
+int ctoi(char c) { return c - '0'; }
+
+double strtod(const char *restrict nptr, char **restrict endptr) {
+ double r = 0;
+ // An initial, possibly empty, sequence of white-space characters (as
+ // specified by isspace())
+ for (; isspace(*nptr); nptr++)
+ ;
+
+ // A subject sequence interpreted as a floating-point constant or representing
+ // infinity or NaN
+
+ {
+ // The expected form of the subject sequence is an optional '+' or '-' sign
+ int sign = 0;
+ int exp_sign = 0;
+ if ('+' == *nptr) {
+ sign = 0;
+ nptr++;
+ } else if ('-' == *nptr) {
+ sign = 1;
+ nptr++;
+ }
+
+ // A non-empty sequence of decimal digits optionally containing a radix
+ // character
+ double exp = 0;
+ for (; isdigit(*nptr); nptr++) {
+ r *= 10;
+ r += ctoi(*nptr);
+ }
+ if ('.' == *nptr) {
+ double div = 10;
+ for (; isdigit(*nptr); nptr++) {
+ r += ctoi(*nptr) / div;
+ div *= 10;
+ }
+ }
+ r *= (sign) ? (-1) : (1);
+
+ // then an optional exponent part consisting of the character 'e' or
+ // the character 'E'
+ if ('e' == tolower(*nptr)) {
+ // optionally followed by a '+' or '-' character
+ if ('+' == *nptr) {
+ exp_sign = 0;
+ nptr++;
+ } else if ('-' == *nptr) {
+ exp_sign = 1;
+ nptr++;
+ }
+ // and then followed by one or more decimal digits
+ for (; isdigit(*nptr); nptr++) {
+ exp *= 10;
+ exp += ctoi(*nptr);
+ }
+ exp *= (exp_sign) ? (-1) : (1);
+ }
+ assert(0 == exp); // TODO
+ }
+
+ // A final string of one or more unrecognized characters, including the
+ // terminating NUL character of the input string
+ ;
+ return r;
+}
diff --git a/userland/libc/stdlib/strtol.c b/userland/libc/stdlib/strtol.c
new file mode 100644
index 0000000..7aa7760
--- /dev/null
+++ b/userland/libc/stdlib/strtol.c
@@ -0,0 +1,52 @@
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <assert.h>
+
+extern int errno;
+extern int get_value(char c, long base);
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html
+long strtol(const char *str, char **restrict endptr, int base) {
+ long ret_value = 0;
+ if (endptr)
+ *endptr = str;
+ // Ignore inital white-space sequence
+ for (; *str && isspace(*str); str++)
+ ;
+ if (!*str)
+ return ret_value;
+
+ int sign = 0;
+ if ('-' == *str) {
+ // FIXME
+ sign = 1;
+ str++;
+ assert(0);
+ } else if ('+' == *str) {
+ str++;
+ }
+
+ if (0 == base) {
+ // FIXME
+ assert(0);
+ }
+
+ if (2 <= base && 36 >= base) {
+ for (; *str; str++) {
+ ret_value *= base;
+ int val = get_value(*str, base);
+ if (ret_value > LONG_MAX - val) {
+ errno = ERANGE;
+ return 0;
+ }
+ ret_value += val;
+ }
+ } else {
+ errno = EINVAL;
+ return 0;
+ }
+ if (endptr)
+ *endptr = str;
+ return ret_value;
+}
diff --git a/userland/libc/stdlib/strtold.c b/userland/libc/stdlib/strtold.c
new file mode 100644
index 0000000..222464e
--- /dev/null
+++ b/userland/libc/stdlib/strtold.c
@@ -0,0 +1,9 @@
+#include <assert.h>
+#include <stdlib.h>
+
+long double strtold(const char *restrict nptr, char **restrict endptr) {
+ // TODO
+ // I will do this some other day
+ assert(NULL);
+ return 0;
+}
diff --git a/userland/libc/stdlib/strtoul.c b/userland/libc/stdlib/strtoul.c
new file mode 100644
index 0000000..4d9a51d
--- /dev/null
+++ b/userland/libc/stdlib/strtoul.c
@@ -0,0 +1,72 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+extern int errno;
+int get_value(char c, long base) {
+ int r;
+ if (c >= '0' && c <= '9')
+ r = c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ r = c - 'A';
+ else if (c >= 'a' && c <= 'z')
+ r = c - 'a';
+ else
+ return -1;
+ if (r >= base)
+ return -1;
+ return r;
+}
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtoul.html
+unsigned long strtoul(const char *restrict str, char **restrict endptr,
+ int base) {
+ unsigned long ret_value = 0;
+ if (endptr)
+ *endptr = str;
+ // Ignore inital white-space sequence
+ for (; *str && isspace(*str); str++)
+ ;
+ if (!*str)
+ return ret_value;
+
+ int sign = 0;
+ if ('-' == *str) {
+ // FIXME
+ sign = 1;
+ str++;
+ assert(0);
+ } else if ('+' == *str) {
+ str++;
+ }
+
+ if (0 == base) {
+ // FIXME
+ assert(0);
+ }
+
+ if (2 <= base && 36 >= base) {
+ for (; *str; str++) {
+ ret_value *= base;
+ int val = get_value(*str, base);
+ /*
+ if (-1 == val) {
+ errno = ERANGE;
+ return 0;
+ }*/
+ if (ret_value > ULONG_MAX - val) {
+ errno = ERANGE;
+ return 0;
+ }
+
+ ret_value += val;
+ }
+ } else {
+ errno = EINVAL;
+ return 0;
+ }
+ if (endptr)
+ *endptr = str;
+ return ret_value;
+}
diff --git a/userland/libc/stdlib/system.c b/userland/libc/stdlib/system.c
new file mode 100644
index 0000000..d951c5c
--- /dev/null
+++ b/userland/libc/stdlib/system.c
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+int system(const char *command) {
+ if (!command)
+ return NULL;
+ int pid = fork();
+ if (0 == pid) {
+ char *argv[2];
+ argv[0] = "/sh";
+ argv[1] = command;
+ execv("/sh", argv);
+ }
+ // FIXME: Use waitpid
+ int rc;
+ (void)wait(&rc);
+ return rc;
+}
diff --git a/userland/libc/string.h b/userland/libc/string.h
new file mode 100644
index 0000000..f811dba
--- /dev/null
+++ b/userland/libc/string.h
@@ -0,0 +1,17 @@
+#ifndef STRING_H
+#define STRING_H
+#include <stddef.h>
+#include <stdint.h>
+
+char *strerror(int errnum);
+void *memset(void *s, int c, size_t n);
+void *memcpy(void *dest, const void *src, uint32_t n);
+int strcmp(const char *s1, const char *s2);
+char *strcpy(char *dest, const char *src);
+size_t strlen(const char *s);
+size_t strnlen(const char *s, size_t maxlen);
+int sscanf(const char *s, const char *restrict format, ...);
+char *strrchr(const char *s, int c);
+int strncmp(const char *s1, const char *s2, size_t n);
+char *strncpy(char *s1, const char *s2, size_t n);
+#endif
diff --git a/userland/libc/string/memcmp.c b/userland/libc/string/memcmp.c
new file mode 100644
index 0000000..01109b8
--- /dev/null
+++ b/userland/libc/string/memcmp.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+int memcmp(const void *s1, const void *s2, size_t n) {
+ int return_value = 0;
+
+ for (uint32_t i = 0; i < n; i++)
+ if (((unsigned char *)(s1))[i] != ((unsigned char *)(s2))[i])
+ return_value++;
+
+ return return_value;
+}
diff --git a/userland/libc/string/memcpy.c b/userland/libc/string/memcpy.c
new file mode 100644
index 0000000..e19dec9
--- /dev/null
+++ b/userland/libc/string/memcpy.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+void *memcpy(void *dest, const void *src, uint32_t n) {
+ unsigned char *d = dest;
+ const unsigned char *s = src;
+ for (; n; n--)
+ *d++ = *s++;
+ return dest;
+}
diff --git a/userland/libc/string/memmove.c b/userland/libc/string/memmove.c
new file mode 100644
index 0000000..5fc49f7
--- /dev/null
+++ b/userland/libc/string/memmove.c
@@ -0,0 +1,14 @@
+#include <string.h>
+
+// copy bytes in memory with overlapping areas
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memmove.html
+void *memmove(void *s1, const void *s2, size_t n) {
+ // Copying takes place as if the n bytes from the object pointed to by s2 are
+ // first copied into a temporary array of n bytes that does not overlap the
+ // objects pointed to by s1 and s2, and then the n bytes from the temporary
+ // array are copied into the object pointed to by s1.
+ unsigned char tmp[n];
+ memcpy(tmp, s2, n);
+ memcpy(s1, tmp, n);
+ return s1;
+}
diff --git a/userland/libc/string/sscanf.c b/userland/libc/string/sscanf.c
new file mode 100644
index 0000000..28e1ce1
--- /dev/null
+++ b/userland/libc/string/sscanf.c
@@ -0,0 +1,193 @@
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+extern int errno;
+extern int get_value(char c, long base);
+
+long ftnum(FILE *stream, int base, int *error) {
+ char c;
+ long ret_value = 0;
+ *error = 0;
+ // Ignore inital white-space sequence
+ for (;;) {
+ if (EOF == (c = fgetc(stream))) {
+ *error = 1;
+ return 0;
+ }
+
+ if (!isspace(c)) {
+ ungetc(c, stream);
+ break;
+ }
+ }
+ if (c == '\0') {
+ *error = 1;
+ return 0;
+ }
+ if (!isdigit(c)) {
+ *error = 1;
+ return 0;
+ }
+ if (!(2 <= base && 36 >= base)) {
+ *error = 1;
+ return 0;
+ }
+ for (;;) {
+ if (EOF == (c = fgetc(stream)))
+ break;
+ if (c == '\0') {
+ ungetc(c, stream);
+ break;
+ }
+ int val = get_value(c, base);
+ if (-1 == val) {
+ ungetc(c, stream);
+ break;
+ }
+ if (ret_value * base > LONG_MAX - val) {
+ ungetc(c, stream);
+ errno = ERANGE;
+ *error = 1;
+ return 0;
+ }
+ ret_value *= base;
+ ret_value += val;
+ }
+ return ret_value;
+}
+
+int vfscanf(FILE *stream, const char *format, va_list ap) {
+ int rc = 0; // Upon successful completion, these functions shall return the
+ // number of successfully matched and assigned input items
+ int cont = 0;
+ int suppress = 0;
+ for (; *format; format++) {
+ if (*format != '%' && !cont) {
+ char c;
+ if (isspace(*format))
+ continue;
+ if (EOF == (c = fgetc(stream))) {
+ break;
+ }
+ if (*format == c) // TODO: Make sure this is the correct behaviour
+ continue;
+ // TODO: Make sure this is the correct behaviour
+ errno = EINVAL;
+ assert(0);
+ break;
+ }
+
+ if (*format == '%' && !cont) {
+ cont = 1;
+ continue;
+ }
+
+ int is_long = 0;
+ switch (*format) {
+ case 'l':
+ is_long++;
+ assert(is_long < 3);
+ cont = 1;
+ break;
+ case 'i': // Matches an optionally signed integer, whose format is the same
+ // as expected for the subject sequence of strtol() with 0 for the
+ // base argument.
+ case 'd': {
+ // Matches an optionally signed decimal integer, whose format is the
+ // same as expected for the subject sequence of strtol() with the value
+ // 10 for the base argument. In the absence of a size modifier, the
+ // application shall ensure that the corresponding argument is a pointer
+ // to int.
+ int err = 0;
+ int result = ftnum(stream, 10, &err);
+ if (err) {
+ cont = 0;
+ break;
+ }
+ if (!suppress) {
+ if (2 == is_long) {
+ *((long long *)va_arg(ap, long long *)) = result;
+ } else if (1 == is_long) {
+ *((long *)va_arg(ap, long *)) = result;
+ } else {
+ *((int *)va_arg(ap, int *)) = result;
+ }
+ rc++;
+ }
+ assert(0 == err);
+ cont = 0;
+ break;
+ }
+ case 'c': {
+ char result = fgetc(stream);
+ if (!suppress) {
+ *((char *)va_arg(ap, char *)) = result;
+ rc++;
+ }
+ cont = 0;
+ break;
+ }
+ case '*': // Assignment suppression
+ suppress = 1;
+ cont = 1;
+ break;
+ default:
+ printf("vfscanf: Got %c but not supported.\n", *format);
+ assert(0);
+ break;
+ }
+ if (!cont) {
+ suppress = 0;
+ }
+ }
+ return rc;
+}
+
+struct sscanf_cookie {
+ const char *s;
+};
+
+size_t sscanf_read(FILE *f, unsigned char *s, size_t l) {
+ struct sscanf_cookie *c = f->cookie;
+ if (!*(c->s)) {
+ return 0;
+ }
+ size_t r = 0;
+ for (; l && *(c->s); l--, c->s += 1) {
+ *s = *(c->s);
+ s++;
+ r++;
+ }
+ if (!(*(c->s)))
+ f->is_eof = 1;
+ /*
+ memcpy(s, c->s, l);
+ c->s += l;*/
+ return r;
+}
+
+int vsscanf(const char *s, const char *restrict format, va_list ap) {
+ struct sscanf_cookie c = {.s = s};
+ FILE f = {
+ .read = sscanf_read,
+ .cookie = &c,
+ .has_buffered_char = 0,
+ .is_eof = 0,
+ .has_error = 0,
+ .offset_in_file = 0,
+ };
+ return vfscanf(&f, format, ap);
+}
+
+int sscanf(const char *s, const char *restrict format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vsscanf(s, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/string/strcasecmp.c b/userland/libc/string/strcasecmp.c
new file mode 100644
index 0000000..eed337b
--- /dev/null
+++ b/userland/libc/string/strcasecmp.c
@@ -0,0 +1,28 @@
+#include <strings.h>
+
+int strcasecmp(const char *s1, const char *s2) {
+ // The strcmp() function shall compare the string pointed to by s1
+ // to the string pointed to by s2.
+ int l1, l2, rc;
+ l1 = l2 = rc = 0;
+ for (; (*s1 || *s2);) {
+ if (tolower(*s1) != tolower(*s2))
+ rc++;
+ if (*s1) {
+ l1++;
+ s1++;
+ }
+ if (*s2) {
+ l2++;
+ s2++;
+ }
+ }
+
+ // Upon completion, strcmp() shall return an integer greater than,
+ // equal to, or less than 0, if the string pointed to by s1 is
+ // greater than, equal to, or less than the string pointed to by
+ // s2, respectively.
+ if (l2 > l1)
+ return -rc;
+ return rc;
+}
diff --git a/userland/libc/string/strcat.c b/userland/libc/string/strcat.c
new file mode 100644
index 0000000..c430698
--- /dev/null
+++ b/userland/libc/string/strcat.c
@@ -0,0 +1,13 @@
+#include <string.h>
+
+char *strcat(char *s1, const char *s2) {
+ strcpy(s1 + strlen(s1), s2);
+ return s1;
+ /*
+char *r = s1;
+for (; *s1; s1++)
+;
+for (; *s2; s2++, s1++)
+*s1 = *s2;
+return r;*/
+}
diff --git a/userland/libc/string/strchr.c b/userland/libc/string/strchr.c
new file mode 100644
index 0000000..1995547
--- /dev/null
+++ b/userland/libc/string/strchr.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+char *strchr(const char *s, int c) {
+ for (; *s; s++) {
+ if (*s == (char)c)
+ return (char*)s;
+ }
+ if ((char)c == '\0')
+ return (char *)s;
+ return NULL;
+}
diff --git a/userland/libc/string/strcmp.c b/userland/libc/string/strcmp.c
new file mode 100644
index 0000000..368b8fb
--- /dev/null
+++ b/userland/libc/string/strcmp.c
@@ -0,0 +1,29 @@
+#include <string.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+int strcmp(const char *s1, const char *s2) {
+ // The strcmp() function shall compare the string pointed to by s1
+ // to the string pointed to by s2.
+ int l1, l2, rc;
+ l1 = l2 = rc = 0;
+ for (; *s1 || *s2;) {
+ if (*s1 != *s2)
+ rc++;
+ if (*s1) {
+ l1++;
+ s1++;
+ }
+ if (*s2) {
+ l2++;
+ s2++;
+ }
+ }
+
+ // Upon completion, strcmp() shall return an integer greater than,
+ // equal to, or less than 0, if the string pointed to by s1 is
+ // greater than, equal to, or less than the string pointed to by
+ // s2, respectively.
+ if (l2 > l1)
+ return -rc;
+ return rc;
+}
diff --git a/userland/libc/string/strcpy.c b/userland/libc/string/strcpy.c
new file mode 100644
index 0000000..9023fd1
--- /dev/null
+++ b/userland/libc/string/strcpy.c
@@ -0,0 +1,7 @@
+#include <string.h>
+
+char *strcpy(char *dest, const char *src) {
+ for (; (*dest = *src); dest++, src++)
+ ;
+ return dest;
+}
diff --git a/userland/libc/string/strcspn.c b/userland/libc/string/strcspn.c
new file mode 100644
index 0000000..2ad38d5
--- /dev/null
+++ b/userland/libc/string/strcspn.c
@@ -0,0 +1,14 @@
+#include <string.h>
+
+size_t strcspn(const char *s1, const char *s2) {
+ size_t r = 0;
+ for (; *s1; s1++) {
+ for (const char *t = s2; *t; t++) {
+ if (*s1 == *s2) {
+ r++;
+ break;
+ }
+ }
+ }
+ return r;
+}
diff --git a/userland/libc/string/strdup.c b/userland/libc/string/strdup.c
new file mode 100644
index 0000000..9e22f94
--- /dev/null
+++ b/userland/libc/string/strdup.c
@@ -0,0 +1,15 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+// The strdup() function shall return a pointer to a new string, which is a
+// duplicate of the string pointed to by s. The returned pointer can be passed
+// to free(). A null pointer is returned if the new string cannot be created.
+char *strdup(const char *s) {
+ size_t l = strlen(s);
+ char *r = malloc(l + 1);
+ if (!r)
+ return NULL;
+ strcpy(r, s);
+ return r;
+}
diff --git a/userland/libc/string/strlcpy.c b/userland/libc/string/strlcpy.c
new file mode 100644
index 0000000..a2d3dd9
--- /dev/null
+++ b/userland/libc/string/strlcpy.c
@@ -0,0 +1,20 @@
+#include <string.h>
+
+// Copy string s2 to buffer s1 of size n. At most n-1
+// chars will be copied. Always NUL terminates (unless n == 0).
+// Returns strlen(s2); if retval >= n, truncation occurred.
+size_t *strlcpy(char *s1, const char *s2, size_t n) {
+ size_t tmp_n = n;
+ const char *os2 = s2;
+ for (; tmp_n; tmp_n--) {
+ if ((*s1++ = *s2++) == '\0')
+ break;
+ }
+ if (tmp_n == 0) {
+ if (n != 0)
+ *s1 = '\0'; /* NUL-terminate s1 */
+ while (*s2++)
+ ;
+ }
+ return s2 - os2 - 1;
+}
diff --git a/userland/libc/string/strlen.c b/userland/libc/string/strlen.c
new file mode 100644
index 0000000..8e3e77a
--- /dev/null
+++ b/userland/libc/string/strlen.c
@@ -0,0 +1,8 @@
+#include <string.h>
+
+size_t strlen(const char *s) {
+ const char *d = s;
+ for (; *s; s++)
+ ;
+ return s - d;
+}
diff --git a/userland/libc/string/strncasecmp.c b/userland/libc/string/strncasecmp.c
new file mode 100644
index 0000000..9ce8c04
--- /dev/null
+++ b/userland/libc/string/strncasecmp.c
@@ -0,0 +1,29 @@
+#include <strings.h>
+#include <stddef.h>
+
+int strncasecmp(const char *s1, const char *s2, size_t n) {
+ // The strcmp() function shall compare the string pointed to by s1
+ // to the string pointed to by s2.
+ int l1, l2, rc;
+ l1 = l2 = rc = 0;
+ for (; (*s1 || *s2) && n > 0; n--) {
+ if (tolower(*s1) != tolower(*s2))
+ rc++;
+ if (*s1) {
+ l1++;
+ s1++;
+ }
+ if (*s2) {
+ l2++;
+ s2++;
+ }
+ }
+
+ // Upon completion, strcmp() shall return an integer greater than,
+ // equal to, or less than 0, if the string pointed to by s1 is
+ // greater than, equal to, or less than the string pointed to by
+ // s2, respectively.
+ if (l2 > l1)
+ return -rc;
+ return rc;
+}
diff --git a/userland/libc/string/strncmp.c b/userland/libc/string/strncmp.c
new file mode 100644
index 0000000..fd46189
--- /dev/null
+++ b/userland/libc/string/strncmp.c
@@ -0,0 +1,28 @@
+#include <string.h>
+
+int strncmp(const char *s1, const char *s2, size_t n) {
+ // The strcmp() function shall compare the string pointed to by s1
+ // to the string pointed to by s2.
+ int l1, l2, rc;
+ l1 = l2 = rc = 0;
+ for (; (*s1 || *s2) && n > 0; n--) {
+ if (*s1 != *s2)
+ rc++;
+ if (*s1) {
+ l1++;
+ s1++;
+ }
+ if (*s2) {
+ l2++;
+ s2++;
+ }
+ }
+
+ // Upon completion, strcmp() shall return an integer greater than,
+ // equal to, or less than 0, if the string pointed to by s1 is
+ // greater than, equal to, or less than the string pointed to by
+ // s2, respectively.
+ if (l2 > l1)
+ return -rc;
+ return rc;
+}
diff --git a/userland/libc/string/strncpy.c b/userland/libc/string/strncpy.c
new file mode 100644
index 0000000..0e88c63
--- /dev/null
+++ b/userland/libc/string/strncpy.c
@@ -0,0 +1,13 @@
+#include <string.h>
+
+char *strncpy(char *s1, const char *s2, size_t n) {
+ char *rc = s1;
+ for (; n > 0; s1++, s2++, n--) {
+ *s1 = *s2;
+ if (!*s2)
+ break;
+ }
+ for (; n > 0; n--,s1++)
+ *s1 = '\0';
+ return rc;
+}
diff --git a/userland/libc/string/strndup.c b/userland/libc/string/strndup.c
new file mode 100644
index 0000000..ffb2088
--- /dev/null
+++ b/userland/libc/string/strndup.c
@@ -0,0 +1,22 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+// The strndup() function shall be equivalent to the strdup() function,
+// duplicating the provided s in a new block of memory allocated as if
+// by using malloc(), with the exception being that strndup() copies at
+// most size plus one bytes into the newly allocated memory, terminating
+// the new string with a NUL character. If the length of s is larger
+// than size, only size bytes shall be duplicated. If size is larger
+// than the length of s, all bytes in s shall be copied into the new
+// memory buffer, including the terminating NUL character. The newly
+// created string shall always be properly terminated.
+char *strndup(const char *s, size_t size) {
+ size_t l = strlen(s);
+ size_t real_l = min(l, size);
+ char *r = malloc(real_l + 1);
+ if (!r)
+ return NULL;
+ strlcpy(r, s, real_l);
+ return r;
+}
diff --git a/userland/libc/string/strnlen.c b/userland/libc/string/strnlen.c
new file mode 100644
index 0000000..86df42e
--- /dev/null
+++ b/userland/libc/string/strnlen.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+size_t strnlen(const char *s, size_t maxlen) {
+ size_t i;
+ for (i = 0; i < maxlen; i++)
+ if (s[i] == 0)
+ break;
+ return i;
+}
diff --git a/userland/libc/string/strpbrk.c b/userland/libc/string/strpbrk.c
new file mode 100644
index 0000000..fb16b0c
--- /dev/null
+++ b/userland/libc/string/strpbrk.c
@@ -0,0 +1,12 @@
+#include <string.h>
+
+char *strpbrk(const char *s1, const char *s2) {
+ for (; *s1; s1++) {
+ for (const char *t = s2; *t; t++) {
+ if (*s1 == *t) {
+ return s1;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/userland/libc/string/strrchr.c b/userland/libc/string/strrchr.c
new file mode 100644
index 0000000..a44199a
--- /dev/null
+++ b/userland/libc/string/strrchr.c
@@ -0,0 +1,12 @@
+#include <string.h>
+
+char *strrchr(const char *s, int c) {
+ char *last = NULL;
+ for (; *s; s++) {
+ if (*s == (char)c)
+ last = (char *)s;
+ }
+ if ((char)c == '\0')
+ last = (char*)s;
+ return last;
+}
diff --git a/userland/libc/string/strspn.c b/userland/libc/string/strspn.c
new file mode 100644
index 0000000..2a7d3ae
--- /dev/null
+++ b/userland/libc/string/strspn.c
@@ -0,0 +1,18 @@
+#include <string.h>
+
+size_t strspn(const char *s1, const char *s2) {
+ size_t r = 0;
+ for (; *s1; s1++) {
+ int e = 0;
+ for (const char *t = s2; *t; t++) {
+ if (*s1 == *t) {
+ e = 1;
+ break;
+ }
+ }
+ if (!e)
+ break;
+ r++;
+ }
+ return r;
+}
diff --git a/userland/libc/string/strstr.c b/userland/libc/string/strstr.c
new file mode 100644
index 0000000..20b9dc2
--- /dev/null
+++ b/userland/libc/string/strstr.c
@@ -0,0 +1,21 @@
+#include <string.h>
+
+char *strstr(const char *s1, const char *s2) {
+ // If s2 points to a string with zero length, the function shall return s1.
+ if ('\0' == *s2)
+ return s1;
+ for (; *s1; s1++) {
+ const char *t1 = s1;
+ const char *t2 = s2;
+ int is_dif = 0;
+ for (; *t2 && *t1; t1++, t2++) {
+ if (*t2 != *t1) {
+ is_dif = 1;
+ break;
+ }
+ }
+ if (!is_dif)
+ return s1;
+ }
+ return NULL;
+}
diff --git a/userland/libc/string/strtok.c b/userland/libc/string/strtok.c
new file mode 100644
index 0000000..7b8d0e3
--- /dev/null
+++ b/userland/libc/string/strtok.c
@@ -0,0 +1,26 @@
+#include <string.h>
+
+char *strtok_s;
+
+char *strtok(char *restrict s, const char *restrict sep) {
+ if (s) {
+ strtok_s = s;
+ return strtok(NULL, sep);
+ }
+ if(!strtok_s)
+ return NULL;
+ char *e = strpbrk(strtok_s, sep);
+ if (!e) {
+ char *r = strtok_s;
+ strtok_s = NULL;
+ return r;
+ }
+ *e = '\0';
+ e--;
+ for (; *sep; sep++)
+ e++;
+ e++;
+ char *r = strtok_s;
+ strtok_s = e;
+ return r;
+}
diff --git a/userland/libc/strings.h b/userland/libc/strings.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/strings.h
diff --git a/userland/libc/sys/mman.h b/userland/libc/sys/mman.h
new file mode 100644
index 0000000..e3ff734
--- /dev/null
+++ b/userland/libc/sys/mman.h
@@ -0,0 +1,8 @@
+#ifndef MMAP_H
+#define MMAP_H
+#include <stdint.h>
+#include <stddef.h>
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ size_t offset);
+#endif
diff --git a/userland/libc/sys/mman/mmap.c b/userland/libc/sys/mman/mmap.c
new file mode 100644
index 0000000..b9ad3a2
--- /dev/null
+++ b/userland/libc/sys/mman/mmap.c
@@ -0,0 +1,19 @@
+#include <syscall.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+extern int errno;
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ size_t offset) {
+ SYS_MMAP_PARAMS args = {
+ .addr = addr,
+ .length = length,
+ .prot = prot,
+ .flags = flags,
+ .fd = fd,
+ .offset = offset,
+ };
+// return (void*)syscall(SYS_MMAP, &args, 0, 0, 0, 0);
+ RC_ERRNO(syscall(SYS_MMAP, &args, 0, 0, 0, 0));
+}
diff --git a/userland/libc/sys/stat.h b/userland/libc/sys/stat.h
new file mode 100644
index 0000000..2fe6cdc
--- /dev/null
+++ b/userland/libc/sys/stat.h
@@ -0,0 +1,30 @@
+#ifndef STAT_H
+#define STAT_H
+#include <sys/types.h>
+#include <time.h>
+
+struct stat {
+ dev_t st_dev; // Device ID of device containing file.
+ ino_t st_ino; // File serial number.
+ mode_t st_mode; // Mode of file (see below).
+ nlink_t st_nlink; // Number of hard links to the file.
+ uid_t st_uid; // User ID of file.
+ gid_t st_gid; // Group ID of file.
+ dev_t st_rdev; // Device ID (if file is character or block special).
+ off_t st_size; // For regular files, the file size in bytes.
+ // For symbolic links, the length in bytes of the
+ // pathname contained in the symbolic link.
+ // For a shared memory object, the length in bytes.
+ // For a typed memory object, the length in bytes.
+ // For other file types, the use of this field is
+ // unspecified.
+ struct timespec st_atime; // Last data access timestamp.
+ struct timespec st_mtime; // Last data modification timestamp.
+ struct timespec st_ctime; // Last file status change timestamp.
+ blksize_t st_blksize; // A file system-specific preferred I/O block size
+ // for this object. In some file system types, this
+ // may vary from file to file.
+ blkcnt_t st_blocks; // Number of blocks allocated for this object.
+};
+int stat(const char *path, struct stat *buf);
+#endif
diff --git a/userland/libc/sys/stat/mkdir.c b/userland/libc/sys/stat/mkdir.c
new file mode 100644
index 0000000..c057fa7
--- /dev/null
+++ b/userland/libc/sys/stat/mkdir.c
@@ -0,0 +1,9 @@
+#include <sys/stat.h>
+#include <assert.h>
+
+int mkdir(const char *path, mode_t mode) {
+ (void)path;
+ (void)mode;
+ assert(0); // TODO: Implement
+ return 0;
+}
diff --git a/userland/libc/sys/stat/stat.c b/userland/libc/sys/stat/stat.c
new file mode 100644
index 0000000..e37223e
--- /dev/null
+++ b/userland/libc/sys/stat/stat.c
@@ -0,0 +1,13 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <syscall.h>
+
+int stat(const char *path, struct stat *buf) {
+ SYS_STAT_PARAMS args = {
+ .pathname = path,
+ .statbuf = buf,
+ };
+ RC_ERRNO(syscall(SYS_STAT, &args, 0, 0, 0, 0));
+}
diff --git a/userland/libc/sys/time/gettimeofday.c b/userland/libc/sys/time/gettimeofday.c
new file mode 100644
index 0000000..e172cf2
--- /dev/null
+++ b/userland/libc/sys/time/gettimeofday.c
@@ -0,0 +1,5 @@
+#include <sys/time.h>
+#include <time.h>
+
+static int return_tod =0;
+int gettimeofday(struct timeval *tp, void *tzp) {return return_tod++;}
diff --git a/userland/libc/sys/types.h b/userland/libc/sys/types.h
new file mode 100644
index 0000000..ffcf281
--- /dev/null
+++ b/userland/libc/sys/types.h
@@ -0,0 +1,27 @@
+#ifndef TYPES_H
+#define TYPES_H
+typedef unsigned int ino_t;
+
+typedef int mode_t;
+
+typedef int nlink_t;
+typedef int uid_t;
+typedef int gid_t;
+typedef int id_t;
+
+typedef int blkcnt_t;
+typedef int off_t;
+
+typedef int dev_t;
+typedef unsigned int fsblkcnt_t;
+typedef unsigned int fsfilcnt_t;
+typedef unsigned int ino_t;
+//typedef unsigned int size_t;
+
+typedef int blksize_t;
+typedef int pid_t;
+typedef int ssize_t;
+
+//typedef int clock_t;
+typedef int time_t;
+#endif
diff --git a/userland/libc/syscall.h b/userland/libc/syscall.h
new file mode 100644
index 0000000..952492f
--- /dev/null
+++ b/userland/libc/syscall.h
@@ -0,0 +1,149 @@
+#ifndef SYSCALL_H
+#define SYSCALL_H
+#include "socket.h"
+#include <stddef.h>
+#include <stdint.h>
+
+#define SYS_OPEN 0
+#define SYS_READ 1
+#define SYS_WRITE 2
+#define SYS_PREAD 3
+#define SYS_PWRITE 4
+#define SYS_FORK 5
+#define SYS_EXEC 6
+#define SYS_GETPID 7
+#define SYS_EXIT 8
+#define SYS_WAIT 9
+#define SYS_BRK 10
+#define SYS_SBRK 11
+#define SYS_PIPE 12
+#define SYS_DUP2 13
+#define SYS_CLOSE 14
+#define SYS_OPENPTY 15
+#define SYS_POLL 16
+#define SYS_MMAP 17
+#define SYS_ACCEPT 18
+#define SYS_BIND 19
+#define SYS_SOCKET 20
+#define SYS_SHM_OPEN 21
+#define SYS_FTRUNCATE 22
+#define SYS_STAT 23
+#define SYS_MSLEEP 24
+#define SYS_UPTIME 25
+
+int syscall(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx,
+ uint32_t esi, uint32_t edi);
+int s_syscall(int sys);
+
+extern int errno;
+#define RC_ERRNO(_rc) \
+ { \
+ int c = _rc; \
+ if (c < 0) { \
+ errno = -(c); \
+ return -1; \
+ } \
+ return c; \
+ }
+
+typedef int mode_t;
+
+typedef struct SYS_OPEN_PARAMS {
+ char *file;
+ int flags;
+ int mode;
+} __attribute__((packed)) SYS_OPEN_PARAMS;
+
+typedef struct SYS_PREAD_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PREAD_PARAMS;
+
+typedef struct SYS_READ_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_READ_PARAMS;
+
+typedef struct SYS_PWRITE_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PWRITE_PARAMS;
+
+typedef struct SYS_WRITE_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_WRITE_PARAMS;
+
+typedef struct SYS_EXEC_PARAMS {
+ char *path;
+ char **argv;
+} __attribute__((packed)) SYS_EXEC_PARAMS;
+
+typedef struct SYS_DUP2_PARAMS {
+ int org_fd;
+ int new_fd;
+} __attribute__((packed)) SYS_DUP2_PARAMS;
+
+typedef struct SYS_OPENPTY_PARAMS {
+ int *amaster;
+ int *aslave;
+ char *name;
+ /*const struct termios*/ void *termp;
+ /*const struct winsize*/ void *winp;
+} __attribute__((packed)) SYS_OPENPTY_PARAMS;
+
+typedef struct SYS_POLL_PARAMS {
+ struct pollfd *fds;
+ size_t nfds;
+ int timeout;
+} __attribute__((packed)) SYS_POLL_PARAMS;
+
+typedef struct SYS_MMAP_PARAMS {
+ void *addr;
+ size_t length;
+ int prot;
+ int flags;
+ int fd;
+ size_t offset;
+} __attribute__((packed)) SYS_MMAP_PARAMS;
+
+typedef struct SYS_SOCKET_PARAMS {
+ int domain;
+ int type;
+ int protocol;
+} __attribute__((packed)) SYS_SOCKET_PARAMS;
+
+typedef struct SYS_BIND_PARAMS {
+ int sockfd;
+ const struct sockaddr *addr;
+ socklen_t addrlen;
+} __attribute__((packed)) SYS_BIND_PARAMS;
+
+typedef struct SYS_ACCEPT_PARAMS {
+ int socket;
+ struct sockaddr *address;
+ socklen_t *address_len;
+} __attribute__((packed)) SYS_ACCEPT_PARAMS;
+
+typedef struct SYS_SHM_OPEN_PARAMS {
+ const char *name;
+ int oflag;
+ mode_t mode;
+} __attribute__((packed)) SYS_SHM_OPEN_PARAMS;
+
+typedef struct SYS_FTRUNCATE_PARAMS {
+ int fildes;
+ uint64_t length;
+} __attribute__((packed)) SYS_FTRUNCATE_PARAMS;
+
+typedef struct SYS_STAT_PARAMS {
+ const char *pathname;
+ struct stat *statbuf;
+} __attribute__((packed)) SYS_STAT_PARAMS;
+#endif
diff --git a/userland/libc/time.h b/userland/libc/time.h
new file mode 100644
index 0000000..771e430
--- /dev/null
+++ b/userland/libc/time.h
@@ -0,0 +1,20 @@
+#include <sys/types.h>
+
+struct tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ long __tm_gmtoff;
+ const char *__tm_zone;
+};
+
+struct timespec {
+ time_t tv_sec; // Seconds.
+ long tv_nsec; // Nanoseconds.
+};
diff --git a/userland/libc/time/clock_gettime.c b/userland/libc/time/clock_gettime.c
new file mode 100644
index 0000000..15f0cb7
--- /dev/null
+++ b/userland/libc/time/clock_gettime.c
@@ -0,0 +1,14 @@
+#include <syscall.h>
+#include <time.h>
+
+int clock_gettime(clockid_t clock_id, struct timespec *tp) {
+ tp->tv_sec = 0;
+ tp->tv_nsec = 0;
+ return 0;
+ /*
+SYS_CLOCK_GETTIME_PARAMS args = {
+.clk = clock_id,
+.ts = tp,
+};
+return syscall(SYS_CLOCK_GETTIME, &args);*/
+}
diff --git a/userland/libc/time/ctime_r.c b/userland/libc/time/ctime_r.c
new file mode 100644
index 0000000..66e6416
--- /dev/null
+++ b/userland/libc/time/ctime_r.c
@@ -0,0 +1,13 @@
+#include <string.h>
+#include <time.h>
+
+// TODO: Implement this
+
+// Time, formatting and parsing are some of the most annoying parts of
+// programming. Lets just hope this function is not important
+char *ctime_r(const time_t *clock, char *buf) {
+ (void)clock;
+ size_t l = strlen(buf);
+ memset(buf, '0', l);
+ return buf;
+}
diff --git a/userland/libc/time/gmtime.c b/userland/libc/time/gmtime.c
new file mode 100644
index 0000000..44e0ff3
--- /dev/null
+++ b/userland/libc/time/gmtime.c
@@ -0,0 +1,21 @@
+#include <time.h>
+
+struct tm gmtime_r = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0,
+ .__tm_gmtoff = 0,
+ .__tm_zone = 0,
+};
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime.html
+struct tm *gmtime(const time_t *timer) {
+ // TODO: Implement
+ return &gmtime_r;
+}
diff --git a/userland/libc/time/localtime.c b/userland/libc/time/localtime.c
new file mode 100644
index 0000000..40ca351
--- /dev/null
+++ b/userland/libc/time/localtime.c
@@ -0,0 +1,21 @@
+#include <time.h>
+
+ struct tm localtime_r = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 0,
+ .tm_mon = 0,
+ .tm_year = 0,
+ .tm_wday = 0,
+ .tm_yday = 0,
+ .tm_isdst = 0,
+ .__tm_gmtoff = 0,
+ .__tm_zone = 0,
+ };
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/localtime.html
+struct tm *localtime(const time_t *timer) {
+ // TODO: Implement
+ return &localtime_r;
+}
diff --git a/userland/libc/time/strftime.c b/userland/libc/time/strftime.c
new file mode 100644
index 0000000..30a080d
--- /dev/null
+++ b/userland/libc/time/strftime.c
@@ -0,0 +1,7 @@
+#include <time.h>
+#include <stddef.h>
+
+size_t strftime(char *restrict s, size_t maxsize,
+ const char *restrict format, const struct tm *restrict timeptr) {
+ return 0;
+}
diff --git a/userland/libc/time/time.c b/userland/libc/time/time.c
new file mode 100644
index 0000000..9931b71
--- /dev/null
+++ b/userland/libc/time/time.c
@@ -0,0 +1,9 @@
+#include <time.h>
+
+time_t time(time_t *tloc) {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ if (tloc)
+ *tloc = ts.tv_sec;
+ return ts.tv_sec;
+}
diff --git a/userland/libc/ubsan.c b/userland/libc/ubsan.c
new file mode 100644
index 0000000..9ac9879
--- /dev/null
+++ b/userland/libc/ubsan.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <ubsan.h>
+
+void ubsan_log(const char *cause, struct source_location source) {
+ printf("UBSAN\n");
+ printf("%s: %s : %d\n", cause, source.file_name, source.line);
+ for (;;)
+ ;
+}
+
+void __ubsan_handle_shift_out_of_bounds(struct ShiftOutOfBoundsData *data,
+ unsigned long lhs, unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_shift_out_of_bounds", data->location);
+}
+
+void __ubsan_handle_add_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_add_overflow", data->location);
+}
+
+void __ubsan_handle_sub_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_sub_overflow", data->location);
+}
+
+void __ubsan_handle_negate_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_negate_overflow", data->location);
+}
+
+void __ubsan_handle_mul_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_mul_overflow", data->location);
+}
+
+void __ubsan_handle_out_of_bounds(struct OutOfBoundsData *data, void *index) {
+ (void)index;
+ ubsan_log("handle_out_of_bounds", data->location);
+}
diff --git a/userland/libc/ubsan.h b/userland/libc/ubsan.h
new file mode 100644
index 0000000..dac5407
--- /dev/null
+++ b/userland/libc/ubsan.h
@@ -0,0 +1,79 @@
+#include <stdint.h>
+
+enum { type_kind_int = 0, type_kind_float = 1, type_unknown = 0xffff };
+
+struct type_descriptor {
+ uint16_t type_kind;
+ uint16_t type_info;
+ char type_name[1];
+};
+
+struct source_location {
+ const char *file_name;
+ union {
+ unsigned long reported;
+ struct {
+ uint32_t line;
+ uint32_t column;
+ };
+ };
+};
+
+struct OverflowData {
+ struct source_location location;
+ struct type_descriptor *type;
+};
+
+struct type_mismatch_data {
+ struct source_location location;
+ struct type_descriptor *type;
+ unsigned long alignment;
+ unsigned char type_check_kind;
+};
+
+struct type_mismatch_data_v1 {
+ struct source_location location;
+ struct type_descriptor *type;
+ unsigned char log_alignment;
+ unsigned char type_check_kind;
+};
+
+struct type_mismatch_data_common {
+ struct source_location *location;
+ struct type_descriptor *type;
+ unsigned long alignment;
+ unsigned char type_check_kind;
+};
+
+struct nonnull_arg_data {
+ struct source_location location;
+ struct source_location attr_location;
+ int arg_index;
+};
+
+struct OutOfBoundsData {
+ struct source_location location;
+ struct type_descriptor *array_type;
+ struct type_descriptor *index_type;
+};
+
+struct ShiftOutOfBoundsData {
+ struct source_location location;
+ struct type_descriptor *lhs_type;
+ struct type_descriptor *rhs_type;
+};
+
+struct unreachable_data {
+ struct source_location location;
+};
+
+struct invalid_value_data {
+ struct source_location location;
+ struct type_descriptor *type;
+};
+
+struct alignment_assumption_data {
+ struct source_location location;
+ struct source_location assumption_location;
+ struct type_descriptor *type;
+};
diff --git a/userland/libc/unistd.h b/userland/libc/unistd.h
new file mode 100644
index 0000000..b1e3434
--- /dev/null
+++ b/userland/libc/unistd.h
@@ -0,0 +1,14 @@
+#ifndef UNISTD_H
+#define UNISTD_H
+#include <stddef.h>
+
+extern int opterr, optind, optopt;
+extern char *optarg;
+
+int close(int fildes);
+int ftruncate(int fildes, size_t length);
+int execv(char *path, char **argv);
+int pipe(int fd[2]);
+int dup2(int org_fd, int new_fd);
+int getopt(int argc, char * const argv[], const char *optstring);
+#endif
diff --git a/userland/libc/unistd/_exit.c b/userland/libc/unistd/_exit.c
new file mode 100644
index 0000000..c6d64be
--- /dev/null
+++ b/userland/libc/unistd/_exit.c
@@ -0,0 +1,8 @@
+#include <syscall.h>
+#include <unistd.h>
+
+// FIXME: Technically exit and _exit are different but this
+// stackoverflow answer says that it does not usually matter. So lets
+// hope that is the case.
+// https://stackoverflow.com/a/5423108
+void _exit(int status) { syscall(SYS_EXIT, (void *)status, 0, 0, 0, 0); }
diff --git a/userland/libc/unistd/execvp.c b/userland/libc/unistd/execvp.c
new file mode 100644
index 0000000..573e822
--- /dev/null
+++ b/userland/libc/unistd/execvp.c
@@ -0,0 +1,8 @@
+#include <unistd.h>
+#include <syscall.h>
+
+// FIXME: Path resolution
+int execvp(const char *file, char *const argv[]) {
+ struct SYS_EXEC_PARAMS args = {.path = file, .argv = argv};
+ return syscall(SYS_EXEC, &args, 0, 0, 0, 0);
+}
diff --git a/userland/libc/unistd/getopt.c b/userland/libc/unistd/getopt.c
new file mode 100644
index 0000000..a026b59
--- /dev/null
+++ b/userland/libc/unistd/getopt.c
@@ -0,0 +1,14 @@
+#include <assert.h>
+#include <unistd.h>
+
+int opterr, optind, optopt;
+char *optarg;
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html
+int getopt(int argc, char *const argv[], const char *optstring) {
+ // TODO
+ optind = 1;
+ optarg = NULL;
+ // assert(0);
+ return -1;
+}
diff --git a/userland/libc/unistd/getpid.c b/userland/libc/unistd/getpid.c
new file mode 100644
index 0000000..8aeef10
--- /dev/null
+++ b/userland/libc/unistd/getpid.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include <syscall.h>
+#include <sys/types.h>
+
+pid_t getpid(void) {
+ return s_syscall(SYS_GETPID);
+}
diff --git a/userland/libc/unistd/msleep.c b/userland/libc/unistd/msleep.c
new file mode 100644
index 0000000..0b016c6
--- /dev/null
+++ b/userland/libc/unistd/msleep.c
@@ -0,0 +1,6 @@
+// Not standard, but it feels like it should be.
+#include <syscall.h>
+#include <unistd.h>
+#include <stdint.h>
+
+void msleep(uint32_t ms) { syscall(SYS_MSLEEP, (void *)ms, 0, 0, 0, 0); }
diff --git a/userland/libc/unistd/unlink.c b/userland/libc/unistd/unlink.c
new file mode 100644
index 0000000..ccac0df
--- /dev/null
+++ b/userland/libc/unistd/unlink.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int unlink(const char *path) {
+ // TODO
+ printf("TODO: Implement unlink");
+ return 0;
+}
diff --git a/userland/libc/unistd/uptime.c b/userland/libc/unistd/uptime.c
new file mode 100644
index 0000000..090a0e5
--- /dev/null
+++ b/userland/libc/unistd/uptime.c
@@ -0,0 +1,6 @@
+// Not standard, but it feels like it should be.
+#include <stdint.h>
+#include <syscall.h>
+#include <unistd.h>
+
+uint32_t uptime(void) { return syscall(SYS_UPTIME, 0, 0, 0, 0, 0); }
diff --git a/userland/libc/wchar.h b/userland/libc/wchar.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/wchar.h
diff --git a/userland/libc/wctype.h b/userland/libc/wctype.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userland/libc/wctype.h