summaryrefslogtreecommitdiff
path: root/userland
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
Move everything into a new repo.
Diffstat (limited to 'userland')
-rw-r--r--userland/ante/Makefile15
-rw-r--r--userland/ante/ante.c296
-rw-r--r--userland/cat/Makefile10
-rwxr-xr-xuserland/cat/catbin0 -> 33852 bytes
-rw-r--r--userland/cat/cat.c24
-rw-r--r--userland/init/Makefile10
-rw-r--r--userland/init/crt0_old.c12
-rwxr-xr-xuserland/init/initbin0 -> 31476 bytes
-rw-r--r--userland/init/init.c15
-rw-r--r--userland/json/Makefile18
m---------userland/json/hashmap0
-rw-r--r--userland/json/json.c360
-rw-r--r--userland/json/json.h39
-rw-r--r--userland/json/libjson.abin0 -> 6304 bytes
-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
-rw-r--r--userland/libgui/Makefile17
-rw-r--r--userland/libgui/font.h3
-rw-r--r--userland/libgui/libgui.c290
-rw-r--r--userland/libgui/libgui.h34
-rw-r--r--userland/minibox/Makefile12
-rw-r--r--userland/minibox/minibox.c65
-rw-r--r--userland/minibox/utilities/ascii.c30
-rw-r--r--userland/minibox/utilities/cat.c54
-rw-r--r--userland/minibox/utilities/echo.c30
-rw-r--r--userland/minibox/utilities/ed.c145
-rw-r--r--userland/minibox/utilities/include.h41
-rw-r--r--userland/minibox/utilities/init.c72
-rw-r--r--userland/minibox/utilities/ls.c42
-rw-r--r--userland/minibox/utilities/minibox.c15
-rw-r--r--userland/minibox/utilities/pwd.c12
-rw-r--r--userland/minibox/utilities/touch.c11
-rw-r--r--userland/minibox/utilities/wc.c106
-rw-r--r--userland/minibox/utilities/yes.c13
-rw-r--r--userland/sh/Makefile13
-rwxr-xr-xuserland/sh/shbin0 -> 44776 bytes
-rw-r--r--userland/sh/sh.c224
-rwxr-xr-xuserland/sh/sh_abin0 -> 9200 bytes
-rwxr-xr-xuserland/sh/sh_badbin0 -> 9188 bytes
-rw-r--r--userland/snake/Makefile15
-rw-r--r--userland/snake/snake.c109
-rw-r--r--userland/terminal/Makefile15
-rw-r--r--userland/terminal/term.c166
-rw-r--r--userland/test/Makefile16
-rwxr-xr-xuserland/test/linux.sh4
-rwxr-xr-xuserland/test/local/a.outbin0 -> 35312 bytes
-rw-r--r--userland/test/local/testfile1
-rwxr-xr-xuserland/test/testbin0 -> 86144 bytes
-rw-r--r--userland/test/test.c722
-rw-r--r--userland/windowserver/Makefile16
-rw-r--r--userland/windowserver/draw.c80
-rw-r--r--userland/windowserver/draw.h13
-rw-r--r--userland/windowserver/font.h133
-rw-r--r--userland/windowserver/ws.c375
-rw-r--r--userland/windowserver/ws.h30
266 files changed, 8356 insertions, 0 deletions
diff --git a/userland/ante/Makefile b/userland/ante/Makefile
new file mode 100644
index 0000000..412b302
--- /dev/null
+++ b/userland/ante/Makefile
@@ -0,0 +1,15 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS = -ggdb -O2 -Wall -Wextra -pedantic -static
+LIB=-L../libgui -lgui -lgcc
+INC=-I../libgui/
+BINS=ante
+all: $(BINS)
+
+ante.o: ante.c
+ $(CC) $(CFLAGS) $(INC) $(LIB) -o $@ -c $<
+
+ante: ante.o
+ $(CC) $(CFLAGS) -o $@ $^ $(LIB)
+
+clean:
+ rm $(BINS) *.o
diff --git a/userland/ante/ante.c b/userland/ante/ante.c
new file mode 100644
index 0000000..753e7f2
--- /dev/null
+++ b/userland/ante/ante.c
@@ -0,0 +1,296 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <libgui.h>
+#include <poll.h>
+#include <pty.h>
+#include <socket.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BACKGROUND_COLOR 0x000000
+
+int file_fd;
+char *file_buffer;
+uint64_t file_buffer_size;
+uint64_t file_size = 0;
+uint64_t file_real_position = 0;
+GUI_Window *global_w;
+
+typedef enum {
+ NORMAL,
+ INSERT,
+ COMMAND,
+} editor_mode_t;
+
+editor_mode_t current_mode = NORMAL;
+uint32_t cursor_pos_x = 0;
+uint64_t cursor_pos_y = 0;
+void draw_file(void);
+void write_file(void);
+
+int clamp(int *x, int *y) {
+ int clamped = 0;
+ if (*x < 0) {
+ *x = 0;
+ clamped = 1;
+ }
+ if (*y < 0) {
+ *y = 0;
+ clamped = 1;
+ }
+ if (*x + 8 > global_w->sx) {
+ *x = (global_w->sx - 8) / 8;
+ clamped = 1;
+ }
+ if (*y + 8 > global_w->sy) {
+ *y = (global_w->sy - 8) / 8;
+ clamped = 1;
+ }
+ return clamped;
+}
+
+void insert_char(int pos, char c) {
+ if (0 == file_size) {
+ file_size++;
+ file_buffer[0] = c;
+ return;
+ }
+
+ // Shift the buffer at 'pos' to the right by one.
+ memmove(file_buffer + pos + 1, file_buffer + pos, file_size - pos);
+
+ file_buffer[pos] = c;
+ file_size++;
+}
+
+void delete_char(int pos, int *deleted_newline) {
+ if (0 == pos) {
+ return;
+ }
+ // Delete the characther at 'pos'. This makes sense if 'pos' is
+ // at the end of the file and as a result the shift operation done
+ // later in the function is not overwritting the characther.
+ if ('\n' == file_buffer[pos - 1])
+ *deleted_newline = 1;
+ file_buffer[pos - 1] = 0;
+
+ // Shift the buffer at 'pos' to the left by one
+ memmove(file_buffer + pos - 1, file_buffer + pos, file_size - pos);
+ file_size--;
+}
+
+uint64_t cursor_to_real(int x, int y, int *is_valid) {
+ *is_valid = 0;
+ if (clamp(&x, &y))
+ return file_real_position;
+ if (0 == file_size) {
+ if (0 == x && 0 == y) {
+ *is_valid = 1;
+ return 0;
+ }
+ return 0;
+ }
+ uint64_t p = 0;
+ int cx = 0;
+ int cy = 0;
+ for (; p < file_size; p++) {
+ if (cx == x && cy == y) {
+ *is_valid = 1;
+ break;
+ }
+ if ('\n' == file_buffer[p]) {
+ cx = 0;
+ cy++;
+ continue;
+ }
+ cx++;
+ }
+ if (*is_valid)
+ return p;
+ else
+ return file_real_position;
+}
+
+void key_event(char c) {
+ int x = 0;
+ int y = 0;
+ if (COMMAND == current_mode) {
+ if ('w' == c) {
+ printf("wrote to file\n");
+ write_file();
+ current_mode = NORMAL;
+ return;
+ }
+ if ('q' == c) {
+ close(file_fd);
+ close(global_w->ws_socket);
+ current_mode = NORMAL;
+ exit(0);
+ return;
+ }
+ return;
+ } else if (NORMAL == current_mode) {
+ switch (c) {
+ case 'j':
+ y++;
+ break;
+ case 'k':
+ y--;
+ break;
+ case 'l':
+ x++;
+ break;
+ case 'h':
+ x--;
+ break;
+ case 'c':
+ printf("entering command mode\n");
+ current_mode = COMMAND;
+ return;
+ break;
+ case 'i':
+ current_mode = INSERT;
+ return;
+ break;
+ default:
+ return;
+ }
+ } else {
+ if ('\x1B' == c) {
+ current_mode = NORMAL;
+ return;
+ }
+ if ('\b' == c) {
+ int deleted_newline = 0;
+ delete_char(file_real_position, &deleted_newline);
+ if (deleted_newline) {
+ y--;
+ } else {
+ x--;
+ }
+ GUI_ClearScreen(global_w, BACKGROUND_COLOR);
+ } else {
+ insert_char(file_real_position, c);
+ printf("inserting char: %c\n", c);
+ if ('\n' == c) {
+ cursor_pos_x = 0;
+ y++;
+ } else {
+ x++;
+ }
+ GUI_ClearScreen(global_w, BACKGROUND_COLOR);
+ }
+ }
+
+ int is_valid_position = 0;
+ file_real_position =
+ cursor_to_real(cursor_pos_x + x, cursor_pos_y + y, &is_valid_position);
+ if (!is_valid_position) {
+ return;
+ }
+ GUI_OverwriteFont(global_w, cursor_pos_x * 8, cursor_pos_y * 8,
+ BACKGROUND_COLOR);
+ draw_file();
+ cursor_pos_x += x;
+ cursor_pos_y += y;
+ GUI_OverwriteFont(global_w, cursor_pos_x * 8, cursor_pos_y * 8, 0xFFFFFF);
+
+ GUI_UpdateWindow(global_w);
+}
+
+void draw_file(void) {
+ assert(file_buffer);
+ uint32_t screen_pos_x = 0;
+ uint64_t screen_pos_y = 0;
+ for (uint64_t i = 0; i < file_size; i++) {
+ char c = file_buffer[i];
+ if ('\n' == c) {
+ screen_pos_x = 0;
+ screen_pos_y += 8;
+ continue;
+ }
+ GUI_DrawFont(global_w, screen_pos_x, screen_pos_y, c);
+ screen_pos_x += 8;
+ }
+}
+
+void write_file(void) {
+ assert(file_buffer);
+ pwrite(file_fd, file_buffer, file_size, 0);
+}
+
+int open_file(const char *file) {
+ file_fd = open(file, O_RDWR, O_CREAT);
+ printf("file_fd: %d\n", file_fd);
+ if (0 > file_fd) {
+ perror("open");
+ return 0;
+ }
+
+ file_buffer_size = 0x1000;
+ file_buffer = malloc(file_buffer_size);
+ uint64_t index = 0;
+ assert(file_buffer);
+ for (;;) {
+ char buffer[4096];
+ int rc = read(file_fd, buffer, 4096);
+ if (-1 == rc) {
+ perror("read");
+ assert(0);
+ }
+
+ if (0 == rc)
+ break;
+
+ file_size += rc;
+ if (file_size > file_buffer_size) {
+ file_buffer_size = file_size;
+ file_buffer = realloc(file_buffer, file_buffer_size);
+ assert(file_buffer);
+ }
+
+ for (int i = 0; i < rc; i++, index++) {
+ file_buffer[index] = buffer[i];
+ }
+ }
+ return 1;
+}
+
+void run() {
+ struct pollfd fds[1];
+ fds[0].fd = global_w->ws_socket;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ for (;; fds[0].revents = 0) {
+ poll(fds, 1, 0);
+ if (fds[0].revents & POLLIN) {
+ WS_EVENT e;
+ int rc;
+ if (0 >= (rc = read(global_w->ws_socket, &e, sizeof(e))))
+ continue;
+ if (0 == e.c)
+ continue;
+ key_event(e.c);
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ printf("File has to be specified.\n");
+ return 1;
+ }
+ global_w = GUI_CreateWindow(10, 10, 150 * 4, 150 * 4);
+ assert(global_w);
+ GUI_ClearScreen(global_w, BACKGROUND_COLOR);
+ GUI_UpdateWindow(global_w);
+
+ assert(open_file(argv[1]));
+ draw_file();
+ run();
+ return 0;
+}
diff --git a/userland/cat/Makefile b/userland/cat/Makefile
new file mode 100644
index 0000000..b45fae9
--- /dev/null
+++ b/userland/cat/Makefile
@@ -0,0 +1,10 @@
+CC="/home/anton/opt/cross/bin/i686-elf-gcc"
+CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough
+BINS=cat
+all: $(BINS)
+
+cat.o: cat.c
+ $(CC) $(CFLAGS) -L../libc/ -lc -c cat.c -I../libc/
+
+cat: cat.o
+ $(CC) -shared -o cat -ffreestanding -nostdlib $(CFLAGS) cat.o -L../libc/ -lc -lgcc #-L../libc/c.a
diff --git a/userland/cat/cat b/userland/cat/cat
new file mode 100755
index 0000000..2ba0c4d
--- /dev/null
+++ b/userland/cat/cat
Binary files differ
diff --git a/userland/cat/cat.c b/userland/cat/cat.c
new file mode 100644
index 0000000..b143581
--- /dev/null
+++ b/userland/cat/cat.c
@@ -0,0 +1,24 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+
+int main(int argc, char **argv) {
+ DIR *d = opendir("/");
+ struct dirent *de = readdir(d);
+ printf("x : %x\n", de->d_name[0]);
+ for (;;)
+ ;
+ /*
+ int fd = 0;
+ if (argc < 2)
+ goto read_stdin;
+ for (int i = 1; i < argc; i++) {
+ if ((fd = open(argv[i], O_RDONLY, 0)) == -1) {
+ return 1;
+ }
+ read_stdin:
+ for (char c[4096], int rc; (rc = read(fd, c, 4096) > 0);)
+ write(1, c, rc);
+ }*/
+ return 0;
+}
diff --git a/userland/init/Makefile b/userland/init/Makefile
new file mode 100644
index 0000000..932fcd3
--- /dev/null
+++ b/userland/init/Makefile
@@ -0,0 +1,10 @@
+CC="/home/anton/opt/cross/bin/i686-elf-gcc"
+CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough
+BINS=init
+all: $(BINS)
+
+init.o: init.c
+ $(CC) $(CFLAGS) -I../libc/ -L../libc/ -lc -c init.c
+
+init: init.o
+ $(CC) -shared -o init -ffreestanding -nostdlib $(CFLAGS) init.o -L../libc/ -lc -lgcc #-L../libc/c.a
diff --git a/userland/init/crt0_old.c b/userland/init/crt0_old.c
new file mode 100644
index 0000000..7d1e977
--- /dev/null
+++ b/userland/init/crt0_old.c
@@ -0,0 +1,12 @@
+#include <stdint.h>
+int main(int argc, char **argv);
+//int main();
+
+void _start(int a, int b, int argc, char** argv)
+{
+ (void)a;
+ kprintf("argc : %x\n", &argc);
+ kprintf("argv : %x\n", &argv);
+ for(;;);
+// main(argc, t);
+}
diff --git a/userland/init/init b/userland/init/init
new file mode 100755
index 0000000..3ae9020
--- /dev/null
+++ b/userland/init/init
Binary files differ
diff --git a/userland/init/init.c b/userland/init/init.c
new file mode 100644
index 0000000..78c4420
--- /dev/null
+++ b/userland/init/init.c
@@ -0,0 +1,15 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ if (fork())
+ for (;;)
+ wait(NULL);
+
+ char *a[] = {NULL};
+ execv("/term", a);
+ return 1;
+}
diff --git a/userland/json/Makefile b/userland/json/Makefile
new file mode 100644
index 0000000..9fcb567
--- /dev/null
+++ b/userland/json/Makefile
@@ -0,0 +1,18 @@
+#CC="/home/anton/opt/cross/bin/i686-elf-gcc"
+#AR="/home/anton/opt/cross/bin/i686-elf-ar"
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+AR="/home/anton/prj/osdev/sysroot/bin/i686-sb-ar"
+CFLAGS = -O0 -Wall -Wextra -pedantic -Wimplicit-fallthrough -static -Wno-undef
+BINS=libjson.a
+all: $(BINS)
+LIBS=-L./hashmap -lhashmap
+OBJ=json.o
+
+json.o: json.c
+ $(CC) $(CFLAGS) $(LIBS) -c json.c
+
+libjson.a: $(OBJ)
+ $(AR) rcs libjson.a $^
+
+clean:
+ rm libjson.a json.o
diff --git a/userland/json/hashmap b/userland/json/hashmap
new file mode 160000
+Subproject 07dfe3b606e0d82b2e181b27344b5e4ce8849f3
diff --git a/userland/json/json.c b/userland/json/json.c
new file mode 100644
index 0000000..6449193
--- /dev/null
+++ b/userland/json/json.c
@@ -0,0 +1,360 @@
+//
+// Copyright (C) 2022 by Anton Kling <anton@kling.gg>
+//
+// SPDX-License-Identifier: BSD-2-Clause
+//
+#include "hashmap/hashmap.h"
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum JSON_TYPE {
+ NONE,
+ STRING,
+ NUMBER,
+ OBJECT,
+ ARRAY, // FIXME
+ BOOL,
+ JSON_NULL,
+} JSON_TYPE;
+
+typedef struct JSON_ENTRY {
+ char *name;
+ JSON_TYPE type;
+ void *value;
+} JSON_ENTRY;
+
+typedef struct JSON_OBJECT {
+ size_t size;
+ JSON_ENTRY *entries;
+ HashMap *hash_indexes;
+} JSON_OBJECT;
+
+typedef struct JSON_CTX {
+ JSON_ENTRY global_object;
+} JSON_CTX;
+
+void JSON_Init(JSON_CTX *ctx) {
+ ctx->global_object.name = NULL;
+ ctx->global_object.type = OBJECT;
+ ctx->global_object.value = malloc(sizeof(JSON_OBJECT));
+ JSON_OBJECT *obj = ctx->global_object.value;
+ obj->size = 0;
+ obj->entries = NULL;
+}
+
+const char *skip_whitespace(const char *str) {
+ for (; *str; str++)
+ switch (*str) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+ default:
+ goto _exit;
+ }
+_exit:
+ return str;
+}
+
+char to_lower(char c) {
+ if (c <= 'Z')
+ return c | 32;
+ return c;
+}
+
+int low_strequ(const char *s1, const char *s2, size_t l) {
+ for (; 0 < l; l--, s2++, s1++) {
+ if (to_lower(*s2) != to_lower(*s1))
+ return 0;
+ }
+ return 1;
+}
+
+const char *find_name(const char *json, char *name_buffer, size_t buffer_len,
+ size_t *name_length, int *rc) {
+ *rc = 0;
+ json = skip_whitespace(json);
+ if ('"' != *json)
+ return json;
+
+ // This *can* be the name
+ json++;
+ const char *tmp = json;
+ for (; *tmp && '"' != *tmp; tmp++)
+ ;
+ if (!(*tmp && ':' == *(tmp + 1))) {
+ // Invalid name
+ // FIXME: Do something better than this.
+ assert(0);
+ }
+ // The name was found.
+ *rc = 1;
+ size_t str_len;
+ if (tmp == json)
+ str_len = 0;
+ else
+ str_len = (tmp - json - 1);
+ if (str_len + 1 > buffer_len) {
+ // JSON string is too long for the buffer
+ // FIXME: Do something better than this.
+ assert(0);
+ }
+ memcpy(name_buffer, json, str_len + 1);
+ name_buffer[str_len + 1] = '\0';
+ *name_length = str_len;
+ return tmp + 2;
+}
+
+const char *find_entry(const char *json, char *name_buffer, size_t buffer_len,
+ size_t *name_length, JSON_TYPE *entry_type, int *rc) {
+ *rc = 0;
+ json = skip_whitespace(json);
+ json = find_name(json, name_buffer, 4096, name_length, rc);
+ json = skip_whitespace(json);
+ switch (*json) {
+ case '{':
+ *entry_type = OBJECT;
+ break;
+ case '"':
+ *entry_type = STRING;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ *entry_type = NUMBER;
+ break;
+ case '[':
+ *entry_type = ARRAY;
+ break;
+ case 'T':
+ case 'F':
+ case 't':
+ case 'f':
+ *entry_type = BOOL;
+ break;
+ case 'N':
+ case 'n':
+ *entry_type = JSON_NULL;
+ break;
+ case '}':
+ *rc = 2;
+ break;
+ default:
+ *entry_type = NONE;
+ break;
+ }
+ return json;
+}
+
+const char *extract_int(const char *json, uint32_t **value, int *rc) {
+ *value = malloc(sizeof(uint32_t));
+ *rc = 0;
+ json = skip_whitespace(json);
+ // FIXME: Do some checking to determine that this is a int.
+ **value = 0;
+ for (; *json && ',' != *json && '}' != *json;) {
+ **value *= 10;
+ **value += (uint32_t)(*json - '0');
+ json++;
+ json = skip_whitespace(json);
+ }
+ if ('\0' != *json)
+ *rc = 1;
+ return json;
+}
+
+const char *extract_string(const char *json, char **value, int *rc) {
+ *value = malloc(4096);
+ *rc = 0;
+ int escape = 0;
+ json++;
+ char *tmp = *value;
+ for (; *json; json++) {
+ if ('"' == *json && !escape)
+ break;
+ escape = 0;
+
+ if ('\\' == *json && !escape) {
+ escape = 1;
+ continue;
+ }
+ *tmp = *json;
+ tmp++;
+ }
+ *tmp = '\0';
+ if ('\0' != *json)
+ *rc = 1;
+ return json;
+}
+
+const char *extract_bool(const char *json, uint8_t **value, int *rc) {
+ *value = malloc(sizeof(uint8_t));
+ *rc = 0;
+ if (low_strequ(json, "true", 3)) {
+ **value = 1;
+ json += 4;
+ *rc = 1;
+ return json;
+ } else if (low_strequ(json, "false", 4)) {
+ **value = 0;
+ json += 5;
+ *rc = 1;
+ return json;
+ }
+ return json;
+}
+
+const char *extract_null(const char *json, uint8_t **value, int *rc) {
+ *value = malloc(sizeof(uint8_t));
+ *rc = 0;
+
+ if (low_strequ(json, "null", 3)) {
+ **value = 0;
+ json += 4;
+ *rc = 1;
+ return json;
+ }
+ return json;
+}
+
+void hashmap_free_value(char *key, void *value) {
+ (void)key;
+ free(value);
+}
+
+JSON_ENTRY *JSON_at(JSON_ENTRY *entry, size_t i);
+
+void JSON_Parse(JSON_CTX *ctx, const char *json) {
+ int rc;
+ char name_buffer[4096];
+ size_t name_length;
+ JSON_TYPE entry_type;
+ JSON_OBJECT *object_stack[64];
+ memset(object_stack, 0, sizeof(JSON_OBJECT *[64]));
+ size_t parent_num = 0;
+ object_stack[parent_num] = ctx->global_object.value;
+ object_stack[parent_num]->size = 0;
+
+ for (;; json++) {
+ json = find_entry(json, name_buffer, 4096, &name_length, &entry_type, &rc);
+ if ('\0' == *json)
+ return;
+
+ if (2 == rc) {
+ if (0 == parent_num)
+ return;
+ parent_num--;
+ json++;
+ json = skip_whitespace(json);
+ if (',' == *json)
+ json++;
+ continue;
+ }
+
+ if (NULL == object_stack[parent_num]->entries) {
+ object_stack[parent_num]->entries = malloc(sizeof(JSON_ENTRY[30]));
+ object_stack[parent_num]->size = 0;
+ object_stack[parent_num]->hash_indexes = hashmap_create(30);
+ }
+
+ size_t entry_num = object_stack[parent_num]->size;
+ object_stack[parent_num]->size++;
+
+ if (NULL == object_stack[parent_num]->entries) {
+ void *rc = object_stack[parent_num]->entries =
+ malloc(sizeof(JSON_ENTRY[30]));
+ object_stack[parent_num]->entries = rc;
+ object_stack[parent_num]->size = 0;
+ object_stack[parent_num]->hash_indexes = hashmap_create(30);
+ }
+ JSON_ENTRY *entry = &object_stack[parent_num]->entries[entry_num];
+ if (1 == rc) {
+ name_length++;
+ entry->name = malloc(name_length + 2);
+ memcpy(entry->name, name_buffer, name_length);
+ entry->name[name_length] = '\0';
+ size_t *b = malloc(sizeof(size_t));
+ *b = entry_num;
+ hashmap_add_entry(object_stack[parent_num]->hash_indexes, entry->name, b,
+ hashmap_free_value, 1);
+ } else {
+ entry->name = NULL;
+ }
+ entry->type = entry_type;
+ if (OBJECT == entry_type) {
+ entry->value = malloc(sizeof(JSON_OBJECT));
+ parent_num++;
+ object_stack[parent_num] = entry->value;
+ object_stack[parent_num]->entries = NULL;
+ continue;
+ } else if (NUMBER == entry_type) {
+ json = extract_int(json, (uint32_t **)&entry->value, &rc);
+ } else if (STRING == entry_type) {
+ json = extract_string(json, (char **)&entry->value, &rc);
+ } else if (BOOL == entry_type) {
+ json = extract_bool(json, (uint8_t **)&entry->value, &rc);
+ } else if (JSON_NULL == entry_type) {
+ json = extract_null(json, (uint8_t **)&entry->value, &rc);
+ }
+ json++;
+ }
+}
+
+JSON_ENTRY *JSON_at(JSON_ENTRY *entry, size_t i) {
+ if (OBJECT != entry->type)
+ return NULL;
+ return &((JSON_OBJECT *)(entry->value))->entries[i];
+}
+
+JSON_ENTRY *JSON_search_name(JSON_ENTRY *entry, char *name) {
+ if (OBJECT != entry->type)
+ return NULL;
+ size_t *i =
+ hashmap_get_entry(((JSON_OBJECT *)(entry->value))->hash_indexes, name);
+ if (!i)
+ return NULL;
+ return JSON_at(entry, *i);
+}
+
+void write_entry_content(int fd, size_t indent, JSON_ENTRY *entry) {
+#define PLACE_TABS \
+ { \
+ for (size_t i = 0; i < indent; i++) \
+ dprintf(fd, "\t"); \
+ }
+ PLACE_TABS
+ if (entry->name)
+ dprintf(fd, "\"%s\": ", entry->name);
+ if (OBJECT == entry->type) {
+ JSON_OBJECT *object = entry->value;
+ dprintf(fd, "{\n");
+ for (size_t i = 0; i < object->size; i++)
+ write_entry_content(fd, indent + 1, &object->entries[i]);
+ PLACE_TABS
+ dprintf(fd, "},\n");
+ } else if (NUMBER == entry->type)
+ dprintf(fd, "%d,\n", *(uint32_t *)entry->value);
+ else if (STRING == entry->type)
+ dprintf(fd, "\"%s\",\n", (char *)entry->value);
+ else if (BOOL == entry->type)
+ dprintf(fd, "%s,\n", ((*(uint8_t *)entry->value) ? "true" : "false"));
+ else if (JSON_NULL == entry->type)
+ dprintf(fd, "null,\n");
+}
+
+void JSON_extract_fd(JSON_CTX *ctx, int fd) {
+ JSON_ENTRY *entry = &ctx->global_object;
+ write_entry_content(fd, 0, &((JSON_OBJECT *)entry->value)->entries[0]);
+}
diff --git a/userland/json/json.h b/userland/json/json.h
new file mode 100644
index 0000000..642a878
--- /dev/null
+++ b/userland/json/json.h
@@ -0,0 +1,39 @@
+#ifndef JSON_H
+#define JSON_H
+#include "hashmap/hashmap.h"
+#include <stddef.h>
+#include <stdint.h>
+
+typedef enum JSON_TYPE {
+ NONE,
+ STRING,
+ NUMBER,
+ OBJECT,
+ ARRAY, // FIXME
+ BOOL,
+ JSON_NULL,
+} JSON_TYPE;
+
+typedef struct JSON_ENTRY {
+ char *name;
+ JSON_TYPE type;
+ void *value;
+} JSON_ENTRY;
+
+typedef struct JSON_OBJECT {
+ uint64_t size;
+ JSON_ENTRY *entries;
+ HashMap *hash_indexes;
+} JSON_OBJECT;
+
+typedef struct JSON_CTX {
+ JSON_ENTRY global_object;
+} JSON_CTX;
+
+void JSON_Init(JSON_CTX *ctx);
+void JSON_Parse(JSON_CTX *ctx, const char *json);
+void JSON_extract_fd(JSON_CTX *ctx, int whitespace, int fd);
+uint64_t JSON_extract_length(JSON_CTX *ctx, int whitespace);
+JSON_ENTRY *JSON_at(JSON_ENTRY *entry, uint64_t i);
+JSON_ENTRY *JSON_search_name(JSON_ENTRY *entry, char *name);
+#endif
diff --git a/userland/json/libjson.a b/userland/json/libjson.a
new file mode 100644
index 0000000..ca66650
--- /dev/null
+++ b/userland/json/libjson.a
Binary files differ
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
diff --git a/userland/libgui/Makefile b/userland/libgui/Makefile
new file mode 100644
index 0000000..5a895c4
--- /dev/null
+++ b/userland/libgui/Makefile
@@ -0,0 +1,17 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+AR="/home/anton/prj/osdev/sysroot/bin/i686-sb-ar"
+#CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -nostdlib -static -Wno-undef -fsanitize=shift,signed-integer-overflow,bounds
+CFLAGS = -O2 -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -nostdlib -static -Wno-undef
+BINS=libgui.a
+all: $(BINS)
+LIBS=-L../libc -lc -L../json -ljson -lgcc
+OBJ=libgui.o
+
+libgui.o: libgui.c
+ $(CC) $(CFLAGS) -c libgui.c -I../libc/ -I../json/ $(LIBS)
+
+libgui.a: $(OBJ)
+ $(AR) rcs libgui.a $^
+
+clean:
+ rm $(OBJ) $(BINS)
diff --git a/userland/libgui/font.h b/userland/libgui/font.h
new file mode 100644
index 0000000..763b438
--- /dev/null
+++ b/userland/libgui/font.h
@@ -0,0 +1,3 @@
+#ifndef FONT_H
+#define FONT_H
+#endif
diff --git a/userland/libgui/libgui.c b/userland/libgui/libgui.c
new file mode 100644
index 0000000..5f5526d
--- /dev/null
+++ b/userland/libgui/libgui.c
@@ -0,0 +1,290 @@
+#include "libgui.h"
+#include "font.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define place_pixel_pos(_p, _pos) \
+ { *(uint32_t *)(w->bitmap_ptr + _pos) = _p; }
+
+static const unsigned char font8x8_basic[128][8] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
+ {0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
+ {0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
+ {0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
+ {0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
+ {0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
+ {0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
+ {0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
+ {0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
+ {0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
+ {0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
+ {0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
+ {0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
+ {0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
+ {0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
+ {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
+ {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
+ {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
+ {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
+ {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
+ {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
+ {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
+ {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
+ {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
+ {0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
+ {0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;)
+ {0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
+ {0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
+ {0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
+ {0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
+ {0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
+ {0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
+ {0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
+ {0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
+ {0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
+ {0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
+ {0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
+ {0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
+ {0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
+ {0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
+ {0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
+ {0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
+ {0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
+ {0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
+ {0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
+ {0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
+ {0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
+ {0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
+ {0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
+ {0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
+ {0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
+ {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
+ {0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
+ {0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
+ {0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
+ {0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
+ {0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
+ {0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
+ {0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
+ {0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
+ {0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
+ {0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
+ {0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
+ {0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
+ {0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
+ {0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
+ {0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
+ {0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
+ {0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
+ {0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
+ {0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
+ {0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
+ {0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
+ {0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
+ {0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
+ {0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
+ {0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
+ {0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
+ {0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
+ {0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
+ {0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
+ {0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
+ {0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
+ {0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
+ {0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
+ {0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
+ {0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
+ {0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
+ {0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
+ {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
+ {0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
+ {0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
+};
+
+#define pokeb(S, O, V) (*(unsigned char *)((S) + (O)) = (V))
+
+// Very temporary
+char *random_string(void) {
+ int fd = open("/dev/urandom", O_RDONLY, 0);
+ char *r = malloc(sizeof(char[10]));
+ for (int i = 0; i < 10 - 1; i++) {
+ char c;
+ read(fd, &c, 1);
+ r[i] = 'a' + (c & 0xF);
+ }
+ close(fd);
+ r[9] = '\0';
+ printf("r: %s\n", r);
+ return r;
+}
+
+int get_bitmap_value(const unsigned char bitmap[], int i) {
+ int array_index = i / 8;
+ int byte_index = i % 8;
+ int rc = (bitmap[array_index] >> byte_index) & 0x1;
+ return rc;
+}
+
+void GUI_OverwriteFont(GUI_Window *w, uint32_t px, uint32_t py,
+ const uint32_t color) {
+ int sx, sy;
+ sx = 8;
+ sy = 8;
+ int x, y;
+ x = px;
+ y = py;
+ if (px + sx > w->sx)
+ return;
+ if (py + sy > w->sy)
+ return;
+ for (int i = 0; i < sx * sy; i++) {
+ int pos = x + y * w->sx;
+ place_pixel_pos(color, pos);
+ x++;
+ if (x >= sx + px) {
+ y++;
+ x = px;
+ }
+ if (y > py + sy)
+ break;
+ }
+}
+
+void GUI_DrawFont(GUI_Window *w, uint32_t px, uint32_t py, const uint32_t c) {
+ int sx, sy;
+ sx = 8;
+ sy = 8;
+ int x, y;
+ x = px;
+ y = py;
+ if (px + sx > w->sx)
+ return;
+ if (py + sy > w->sy)
+ return;
+ const unsigned char *bitmap = font8x8_basic[c];
+ for (int i = 0; i < sx * sy; i++) {
+ int pos = x + y * w->sx;
+ if (get_bitmap_value(bitmap, i)) {
+ place_pixel_pos(0xFFFFFF, pos);
+ } else {
+ place_pixel_pos(0x0, pos);
+ }
+ x++;
+ if (x >= sx + px) {
+ y++;
+ x = px;
+ }
+ if (y > py + sy)
+ break;
+ }
+}
+
+typedef struct {
+ uint16_t px;
+ uint16_t py;
+ uint16_t sx;
+ uint16_t sy;
+ uint8_t name_len;
+} WS_EVENT_CREATE;
+
+void GUI_ClearScreen(GUI_Window *w, uint32_t color) {
+ for (int i = 0; i < w->sx * w->sy; i++)
+ w->bitmap_ptr[i] =color;
+}
+
+GUI_Window *GUI_CreateWindow(uint32_t x, uint32_t y, uint32_t sx, uint32_t sy) {
+ GUI_Window *w = malloc(sizeof(GUI_Window));
+ if (!w)
+ return NULL;
+ w->x = x;
+ w->y = y;
+ w->sx = sx;
+ w->sy = sy;
+
+ // Connect to the windowserver
+ int ws_fd = -1;
+ for (; - 1 == ws_fd;) {
+ ws_fd = open("/dev/windowserver", O_RDWR | O_NONBLOCK, 0);
+ }
+ w->ws_socket = ws_fd;
+ char *str = random_string();
+ WS_EVENT_CREATE e;
+ e.px = x;
+ e.py = y;
+ e.sx = sx;
+ e.sy = sy;
+ e.name_len = (uint8_t)strlen(str) + 1;
+
+ // Create bitmap
+ w->bitmap_fd = shm_open(str, O_RDWR, 0);
+ if (!((int)w->bitmap_fd >= 0)) {
+ printf("bitmap_fd: %x\n", w->bitmap_fd);
+ assert(0);
+ }
+ ftruncate(w->bitmap_fd, sx * sy * sizeof(uint32_t));
+ void *rc = mmap(NULL, sx * sy * sizeof(uint32_t), 0, 0, w->bitmap_fd, 0);
+ if (!((int)rc >= 0)) {
+ printf("rc: %x\n", rc);
+ assert(0);
+ }
+ w->bitmap_ptr = rc;
+
+ // Send the request to the windowserver
+ uint8_t l = 1;
+ uint8_t len = sizeof(l) + sizeof(e) + e.name_len;
+ char *buffer = malloc(len);
+ char *p = buffer;
+ memcpy(p, &l, sizeof(l));
+ p += sizeof(l);
+ memcpy(p, &e, sizeof(e));
+ p += sizeof(e);
+ strcpy(p, str);
+ write(ws_fd, buffer, len);
+ return w;
+}
+
+void GUI_UpdateWindow(GUI_Window *w) {
+ uint8_t l = 0;
+ write(w->ws_socket, &l, sizeof(l));
+}
diff --git a/userland/libgui/libgui.h b/userland/libgui/libgui.h
new file mode 100644
index 0000000..d58c23c
--- /dev/null
+++ b/userland/libgui/libgui.h
@@ -0,0 +1,34 @@
+#ifndef LIBGUI_H
+#define LIBGUI_H
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+ int ws_socket;
+ int bitmap_fd;
+ uint32_t *bitmap_ptr;
+ int x;
+ int y;
+ int sx;
+ int sy;
+} GUI_Window;
+
+// Taken from drivers/keyboard.c
+struct KEY_EVENT {
+ char c;
+ uint8_t mode; // (shift (0 bit)) (alt (1 bit))
+ uint8_t release; // 0 pressed, 1 released
+};
+
+typedef struct {
+ int type;
+ struct KEY_EVENT ev;
+} WS_EVENT;
+
+GUI_Window *GUI_CreateWindow(uint32_t x, uint32_t y, uint32_t sx, uint32_t sy);
+void GUI_DrawFont(GUI_Window *w, uint32_t px, uint32_t py, const uint32_t c);
+void GUI_UpdateWindow(GUI_Window *w);
+void GUI_OverwriteFont(GUI_Window *w, uint32_t px, uint32_t py,
+ const uint32_t color);
+void GUI_ClearScreen(GUI_Window *w, uint32_t color);
+#endif
diff --git a/userland/minibox/Makefile b/userland/minibox/Makefile
new file mode 100644
index 0000000..5448f96
--- /dev/null
+++ b/userland/minibox/Makefile
@@ -0,0 +1,12 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS=-Wall -Wextra -pedantic -Wimplicit-fallthrough -g -O0
+OBJ=minibox.o utilities/cat.o utilities/echo.o utilities/yes.o utilities/minibox.o utilities/ascii.o utilities/wc.o utilities/init.o utilities/ls.o utilities/touch.o utilities/ed.o
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDE) $(LIBS) -c $< -o $@
+
+minibox: $(OBJ)
+ $(CC) $(INCLUDE) -o $@ $^ $(CFLAGS) $(LIBS)
+
+clean:
+ rm minibox $(OBJ)
diff --git a/userland/minibox/minibox.c b/userland/minibox/minibox.c
new file mode 100644
index 0000000..63eaee8
--- /dev/null
+++ b/userland/minibox/minibox.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: 0BSD
+// TODO: Possibly use getprogname() instead of using argv[0] to get the
+// utility name.
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utilities/include.h"
+
+#define ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0]))
+
+typedef struct Command {
+ char *name;
+ int (*function)(int, char **);
+} Command;
+
+#define STR2(_x) #_x
+#define STR(_x) STR2(_x)
+#define COMMAND(NAME) \
+ { STR(NAME), NAME##_main }
+
+Command utilities[] = {COMMAND(minibox), COMMAND(ascii), COMMAND(echo),
+ COMMAND(cat), COMMAND(yes), COMMAND(wc),
+ COMMAND(init), COMMAND(ls), COMMAND(touch),
+ COMMAND(ed)};
+
+char *parse_filename(char *str) {
+ char *tmp = NULL, *is = str;
+ for (; *is++;)
+ if ('/' == *is)
+ tmp = is;
+ return tmp ? tmp + 1 : str;
+}
+
+void usage(void) {
+ for (int i = 0; i < ARRAY_LENGTH(utilities); i++) {
+ printf("%s ", utilities[i].name);
+ }
+ printf("\n");
+}
+
+int main(int argc, char **argv) {
+ if (argc < 1)
+ return 1;
+#ifdef SINGLE_MAIN
+ return utilities[0].function(argc, argv);
+#endif
+
+ // argv[0] will be checked to determine what utility
+ // is supposed to be ran.
+ // NOTE: "minibox" is a utility than can be ran. "minibox"
+ // will switch argv and argc so that argv[1] -> argv[0]
+ // then it will rerun main(). This allows utlities
+ // to be ran like "minibox <utility> <arguments>" or
+ // even "minibox minibox <utility> <arguments>"
+ const char *utility_name = parse_filename(argv[0]);
+ if (*utility_name == '/')
+ utility_name++;
+ for (int i = 0; i < ARRAY_LENGTH(utilities); i++)
+ if (0 == strcmp(utility_name, utilities[i].name))
+ return utilities[i].function(argc, argv);
+
+ usage();
+ return 0;
+}
diff --git a/userland/minibox/utilities/ascii.c b/userland/minibox/utilities/ascii.c
new file mode 100644
index 0000000..53429dc
--- /dev/null
+++ b/userland/minibox/utilities/ascii.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+const char * const ascii_table[] =
+{
+"Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex Dec Hex",
+" 0 00 NUL 16 10 DLE 32 20 48 30 0 64 40 @ 80 50 P 96 60 ` 112 70 p",
+" 1 01 SOH 17 11 DC1 33 21 ! 49 31 1 65 41 A 81 51 Q 97 61 a 113 71 q",
+" 2 02 STX 18 12 DC2 34 22 \" 50 32 2 66 42 B 82 52 R 98 62 b 114 72 r",
+" 3 03 ETX 19 13 DC3 35 23 # 51 33 3 67 43 C 83 53 S 99 63 c 115 73 s",
+" 4 04 EOT 20 14 DC4 36 24 $ 52 34 4 68 44 D 84 54 T 100 64 d 116 74 t",
+" 5 05 ENQ 21 15 NAK 37 25 % 53 35 5 69 45 E 85 55 U 101 65 e 117 75 u",
+" 6 06 ACK 22 16 SYN 38 26 & 54 36 6 70 46 F 86 56 V 102 66 f 118 76 v",
+" 7 07 BEL 23 17 ETB 39 27 ' 55 37 7 71 47 G 87 57 W 103 67 g 119 77 w",
+" 8 08 BS 24 18 CAN 40 28 ( 56 38 8 72 48 H 88 58 X 104 68 h 120 78 x",
+" 9 09 HT 25 19 EM 41 29 ) 57 39 9 73 49 I 89 59 Y 105 69 i 121 79 y",
+" 10 0A LF 26 1A SUB 42 2A * 58 3A : 74 4A J 90 5A Z 106 6A j 122 7A z",
+" 11 0B VT 27 1B ESC 43 2B + 59 3B ; 75 4B K 91 5B [ 107 6B k 123 7B {",
+" 12 0C FF 28 1C FS 44 2C , 60 3C < 76 4C L 92 5C \\ 108 6C l 124 7C |",
+" 13 0D CR 29 1D GS 45 2D - 61 3D = 77 4D M 93 5D ] 109 6D m 125 7D }",
+" 14 0E SO 30 1E RS 46 2E . 62 3E > 78 4E N 94 5E ^ 110 6E n 126 7E ~",
+" 15 0F SI 31 1F US 47 2F / 63 3F ? 79 4F O 95 5F _ 111 6F o 127 7F DEL",
+};
+
+int ascii_main(int argc, char ** argv)
+{
+ for(int i = 0;i < sizeof(ascii_table)/sizeof(ascii_table[0]);i++)
+ puts(ascii_table[i]);
+
+ return 0;
+}
diff --git a/userland/minibox/utilities/cat.c b/userland/minibox/utilities/cat.c
new file mode 100644
index 0000000..c528d49
--- /dev/null
+++ b/userland/minibox/utilities/cat.c
@@ -0,0 +1,54 @@
+// cat - concatenate and print files
+// https://pubs.opengroup.org/onlinepubs/9699919799/
+//
+// TODO: Add -u flag "Write bytes from the input file to the standard
+// output without delay as each is read."
+#include "include.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int fd_to_stdout(int fd) {
+ int rc;
+ for (char buffer[CAT_BUFFER]; (rc = read(fd, &buffer, sizeof(buffer)));) {
+ if (-1 == rc)
+ return 0;
+ if (-1 == write(fd_stdout, buffer, rc))
+ return 0;
+ }
+ return 1;
+}
+
+int cat_main(int argc, char **argv) {
+ int fd = fd_stdin;
+
+ // If no file operands are specified, the standard input shall be
+ // used.
+ if (argc < 2) {
+ return (fd_to_stdout(0)) ? 0 : 1;
+ }
+
+ argv++;
+ for (; *argv; argv++) {
+ // If a file is '-', the cat utility shall read from the standard
+ // input at that point in the sequence.
+ if (0 == strcmp(*argv, "-")) {
+ if (!fd_to_stdout(0))
+ return 1;
+ continue;
+ }
+
+ if (-1 == (fd = open(*argv, O_RDONLY, 0))) {
+ printf("cat: ");
+ // fflush(stdout);
+ perror(*argv);
+ return 1;
+ }
+ if (!fd_to_stdout(fd))
+ return 1;
+ close(fd);
+ }
+ return 0;
+}
diff --git a/userland/minibox/utilities/echo.c b/userland/minibox/utilities/echo.c
new file mode 100644
index 0000000..5ec68a7
--- /dev/null
+++ b/userland/minibox/utilities/echo.c
@@ -0,0 +1,30 @@
+#include "include.h"
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int echo(char **str, int new_line) {
+ for (; *str;) {
+ printf("%s", *str);
+ if (*++str)
+ putchar(' ');
+ }
+
+ if (new_line)
+ putchar('\n');
+ return 0;
+}
+
+int echo_main(int argc, char **argv) {
+ int new_line = 1;
+
+ for (; *++argv && '-' == **argv;)
+ switch (*(*argv + 1)) {
+ case 'n':
+ new_line = 0;
+ break;
+ }
+
+ return echo(argv, new_line);
+}
diff --git a/userland/minibox/utilities/ed.c b/userland/minibox/utilities/ed.c
new file mode 100644
index 0000000..53270dc
--- /dev/null
+++ b/userland/minibox/utilities/ed.c
@@ -0,0 +1,145 @@
+// ed - edit text
+// ed [-p string] [-s] [file]
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#define COMMAND_MODE 0
+#define INPUT_MODE 1
+
+int mode = COMMAND_MODE;
+
+FILE *fp = NULL;
+FILE *mem_fp = NULL;
+int line_number = 1;
+
+int getline(char *buffer, size_t s) {
+ (void)s;
+ int i = 0;
+ char c;
+ for (;; i++) {
+ int rc = read(0, &c, 1);
+ assert(rc > 0);
+ printf("%c", c);
+ buffer[i] = c;
+ if ('\n' == c)
+ break;
+ }
+ buffer[i] = '\0';
+ return i;
+}
+
+void goto_line(void) {
+ char c;
+ fseek(mem_fp, 0, SEEK_SET);
+ int line = 1;
+ if (1 != line_number) {
+ // Goto line
+ for (; fread(&c, 1, 1, mem_fp);) {
+ if ('\n' == c)
+ line++;
+ if (line == line_number)
+ return;
+ }
+ printf("got to line: %d\n", line);
+ assert(0);
+ }
+}
+
+void read_line(void) {
+ char c;
+ goto_line();
+ for (; fread(&c, 1, 1, mem_fp);) {
+ if ('\n' == c)
+ break;
+ printf("%c", c);
+ }
+ printf("\n");
+}
+
+void goto_end_of_line(void) {
+ char c;
+ for (; fread(&c, 1, 1, mem_fp);) {
+ if ('\n' == c)
+ break;
+ }
+}
+
+void delete_line(void) {
+ long s = ftell(mem_fp);
+ printf("s: %d\n", s);
+ goto_end_of_line();
+ long end = ftell(mem_fp);
+ printf("end: %d\n", end);
+ long offset = end - s;
+ for (char buffer[4096];;) {
+ int rc = fread(buffer, 1, 100, mem_fp);
+ long reset = ftell(mem_fp);
+ if (0 == rc)
+ break;
+ fseek(mem_fp, s, SEEK_SET);
+ fwrite(buffer, 1, rc, mem_fp);
+ s += rc;
+ fseek(mem_fp, reset, SEEK_SET);
+ }
+}
+
+void read_command(void) {
+ char buffer[4096];
+ char *s = buffer;
+ getline(buffer, 4096);
+ int a = -1;
+ int rc = sscanf(buffer, "%d", &a);
+ if (0 < rc) {
+ line_number = a;
+ }
+ for (; isdigit(*s); s++)
+ ;
+ if (0 == strcmp(s, "i")) {
+ mode = INPUT_MODE;
+ return;
+ }
+ if (0 == strcmp(s, ".")) {
+ read_line();
+ return;
+ }
+ return;
+}
+
+void read_input(void) {
+ char buffer[4096];
+ int l = getline(buffer, 4096);
+ if (0 == strcmp(buffer, ".")) {
+ mode = COMMAND_MODE;
+ return;
+ }
+ goto_line();
+ delete_line();
+ goto_line();
+ assert(fwrite(buffer, l, 1, mem_fp));
+ return;
+}
+
+int ed_main(char argc, char **argv) {
+ if (argc < 2)
+ return 1;
+ char *buffer;
+ size_t size;
+ mem_fp = open_memstream(&buffer, &size);
+ assert(mem_fp);
+ fp = fopen(argv[1], "r");
+ assert(fp);
+ char r_buffer[4096];
+ for (int rc; (rc = fread(buffer, 1, 4096, fp));) {
+ fwrite(r_buffer, 1, rc, mem_fp);
+ }
+
+ for (;;) {
+ if (COMMAND_MODE == mode)
+ read_command();
+ else
+ read_input();
+ }
+ fclose(fp);
+ fclose(mem_fp);
+ return 0;
+}
diff --git a/userland/minibox/utilities/include.h b/userland/minibox/utilities/include.h
new file mode 100644
index 0000000..7ffd136
--- /dev/null
+++ b/userland/minibox/utilities/include.h
@@ -0,0 +1,41 @@
+#include <stdio.h>
+/* General constants */
+#define fd_stdin 0 /* standard in file descriptor */
+#define fd_stdout 1 /* standard out file descriptor */
+#define ASSERT_NOT_REACHED assert(0);
+/* END General constants */
+
+/* CAT SETTINGS */
+#define CAT_BUFFER 4096 /* size of the read buffer */
+/* END CAT SETTINGS */
+
+/* HTTP SETTINGS */
+#define HTTP_MAX_BUFFER 4096 /* size of the read buffer */
+#define HTTP_DEFAULT_PORT 1337 /* default port should -p not be supplied */
+#define HTTP_WEBSITE_ROOT "site/" /* default directory that it chroots into */
+
+/* should xxx.html not exist these will be supplied instead */
+#define HTTP_DEFAULT_404_SITE "404 - Not Found"
+#define HTTP_DEFAULT_400_SITE "400 - Bad Request"
+/* END HTTP SETTINGS */
+
+#define COND_PERROR_EXP(condition, function_name, expression) \
+ if (condition) { \
+ perror(function_name); \
+ expression; \
+ }
+
+int minibox_main(int argc, char **argv);
+int ascii_main(int argc, char **argv);
+int echo_main(int argc, char **argv);
+int http_main(int argc, char **argv);
+int lock_main(int argc, char **argv);
+int yes_main(int argc, char **argv);
+int cat_main(int argc, char **argv);
+int pwd_main(int argc, char **argv);
+int wc_main(int argc, char **argv);
+int ls_main(int argc, char **argv);
+int touch_main(int argc, char **argv);
+int ed_main(int argc, char **argv);
+
+int init_main(void);
diff --git a/userland/minibox/utilities/init.c b/userland/minibox/utilities/init.c
new file mode 100644
index 0000000..35bedf2
--- /dev/null
+++ b/userland/minibox/utilities/init.c
@@ -0,0 +1,72 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pty.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int cmdfd;
+
+void execsh(void) {
+ char *argv[] = {NULL};
+ execv("/sh", argv);
+}
+
+void newtty(void) {
+ int m;
+ int s;
+ openpty(&m, &s, NULL, NULL, NULL);
+ int pid = fork();
+ if (0 == pid) {
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ execsh();
+ return;
+ }
+ close(s);
+ cmdfd = m;
+}
+
+void shell() {
+ struct pollfd fds[2];
+ fds[0].fd = cmdfd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ fds[1].fd = open("/dev/keyboard", O_RDONLY, 0);
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+ for (;; fds[0].revents = fds[1].revents = 0) {
+ poll(fds, 2, 0);
+ if (fds[0].revents & POLLIN) {
+ char c[4096];
+ int rc = read(fds[0].fd, c, 4096);
+ assert(rc > 0);
+ for (int i = 0; i < rc; i++)
+ putchar(c[i]);
+ }
+ if (fds[1].revents & POLLIN) {
+ char c;
+ int rc = read(fds[1].fd, &c, 1);
+ assert(rc > 0);
+ write(cmdfd, &c, 1);
+ // putchar(c);
+ }
+ }
+}
+
+int init_main(void) {
+ if (1 != getpid()) {
+ printf("minibox init must be launched as pid1.\n");
+ return 1;
+ }
+ if (fork())
+ for (;;)
+ wait(NULL);
+ char *a[] = {NULL};
+ execv("/term", a);
+ return 1;
+}
diff --git a/userland/minibox/utilities/ls.c b/userland/minibox/utilities/ls.c
new file mode 100644
index 0000000..dc93d36
--- /dev/null
+++ b/userland/minibox/utilities/ls.c
@@ -0,0 +1,42 @@
+#include "include.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int ls_main(int argc, char **argv) {
+ int list_hidden = 0;
+ int newline = 0;
+ /*
+ for (int ch;-1 != (ch = getopt(argc, argv, "a1"));)
+ switch ((char)ch)
+ {
+ case 'a':
+ list_hidden = 1;
+ break;
+ case '1':
+ newline = 1;
+ break;
+ }*/
+ struct dirent **namelist;
+ int n;
+ COND_PERROR_EXP(-1 == (n = scandir("/", &namelist, 0, 0)), "scandir",
+ return 1);
+
+ int prev = 0;
+ for (int i = 0; i < n; i++) {
+ if (!list_hidden)
+ if ('.' == *namelist[i]->d_name)
+ continue;
+
+ if (prev)
+ putchar(newline ? '\n' : ' ');
+ else
+ prev = 1;
+ printf("%s", namelist[i]->d_name);
+ }
+ putchar('\n');
+ return 0;
+}
diff --git a/userland/minibox/utilities/minibox.c b/userland/minibox/utilities/minibox.c
new file mode 100644
index 0000000..ec5fdbd
--- /dev/null
+++ b/userland/minibox/utilities/minibox.c
@@ -0,0 +1,15 @@
+#include "include.h"
+
+void usage(void);
+int main(int argc, char **argv);
+
+// Should a command be called via "minibox <command> <arguments>" then shift
+// argv and argc to become "<command> <arguments>" and rerun main()
+int minibox_main(int argc, char **argv) {
+ if (argc < 2)
+ usage();
+
+ argc--;
+ argv++;
+ return main(argc, argv);
+}
diff --git a/userland/minibox/utilities/pwd.c b/userland/minibox/utilities/pwd.c
new file mode 100644
index 0000000..29d8129
--- /dev/null
+++ b/userland/minibox/utilities/pwd.c
@@ -0,0 +1,12 @@
+#include "include.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+
+int pwd_main(int argc, char **argv) {
+ char cwd[PATH_MAX];
+ COND_PERROR_EXP(NULL == (getcwd(cwd, PATH_MAX)), "getcwd", return 1);
+ puts(cwd);
+ return 0;
+}
diff --git a/userland/minibox/utilities/touch.c b/userland/minibox/utilities/touch.c
new file mode 100644
index 0000000..949ad96
--- /dev/null
+++ b/userland/minibox/utilities/touch.c
@@ -0,0 +1,11 @@
+#include "include.h"
+#include <fcntl.h>
+
+int touch_main(int argc, char **argv) {
+ if (argc < 2)
+ return 1;
+ int fd;
+ COND_PERROR_EXP(open(argv[1], 0, O_CREAT), "open", return 1)
+ close(fd);
+ return 0;
+}
diff --git a/userland/minibox/utilities/wc.c b/userland/minibox/utilities/wc.c
new file mode 100644
index 0000000..886eb9e
--- /dev/null
+++ b/userland/minibox/utilities/wc.c
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "include.h"
+
+int cmode, wmode, lmode;
+
+void output(const char * str, size_t lines, size_t words, size_t chars)
+{
+ if(lmode)
+ printf("%d", lines);
+
+ if(wmode)
+ {
+ if(lmode) putchar(' ');
+
+ printf("%d", words);
+ }
+
+ if(cmode)
+ {
+ if(lmode) putchar(' ');
+
+ printf("%d", chars);
+ }
+
+ if(str) printf(" %s", str);
+
+ putchar('\n');
+}
+
+void wc(int argc, char ** argv)
+{
+ int fd = fd_stdin;
+ size_t total_chars, total_words, total_lines;
+ total_chars = total_words = total_lines = 0;
+
+ if(NULL == *argv)
+ goto read_stdin;
+
+
+ for(;*argv;argv++)
+ {
+ int new_word = 1;
+ size_t chars = 0,
+ words = chars,
+ lines = chars;
+
+ fd = open(*argv, O_RDONLY, 0);
+read_stdin:
+ for(char c;read(fd, &c, 1);)
+ {
+ if(cmode)
+ chars++;
+
+ if(wmode)
+ {
+ if(isspace(c)) {
+ new_word = 1;
+ } else if(new_word) {
+ words++;
+ new_word = 0;
+ }
+ }
+
+ if(lmode)
+ if('\n' == c)
+ lines++;
+ }
+ close(fd);
+ if(lmode) total_lines += lines;
+ if(wmode) total_words += words;
+ if(cmode) total_chars += chars;
+
+ output(*argv, lines, words, chars);
+ if(NULL == *argv) return; // We read from stdin
+ }
+
+ if(argc > 1) output("total", total_lines, total_words, total_chars);
+}
+
+int wc_main(int argc, char ** argv)
+{
+ cmode = wmode = lmode = 0;
+ for(argc--;*++argv && '-' == **argv;argc--)
+ switch(*(*argv+1))
+ {
+ case 'l':
+ lmode = 1;
+ break;
+ case 'w':
+ wmode = 1;
+ break;
+ case 'c':
+ cmode = 1;
+ break;
+ }
+
+ if(!(lmode || cmode || wmode))
+ cmode = wmode = lmode = 1;
+
+ wc(argc, argv);
+ return 0;
+}
diff --git a/userland/minibox/utilities/yes.c b/userland/minibox/utilities/yes.c
new file mode 100644
index 0000000..ff19004
--- /dev/null
+++ b/userland/minibox/utilities/yes.c
@@ -0,0 +1,13 @@
+#include "include.h"
+#include <stdio.h>
+
+int yes_main(int argc, char **argv) {
+ if (argc < 2)
+ for (;;)
+ puts("y");
+
+ for (;; putchar('\n'))
+ for (int i = 1; i < argc; i++)
+ printf("%s ", argv[i]);
+ return 0;
+}
diff --git a/userland/sh/Makefile b/userland/sh/Makefile
new file mode 100644
index 0000000..adccfb3
--- /dev/null
+++ b/userland/sh/Makefile
@@ -0,0 +1,13 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -fsanitize=shift,signed-integer-overflow,bounds
+BINS=sh
+all: $(BINS)
+
+sh.o: sh.c
+ $(CC) $(CFLAGS) -L../libc/ -lc -c sh.c -I../libc/
+# $(CC) $(CFLAGS) ../libc/libc.o ../libc/crt0.o sh.c -I../libc/
+
+clean:
+ rm sh sh.o
+sh: sh.o
+ $(CC) -shared -o sh -ffreestanding -nostdlib $(CFLAGS) sh.o -L../libc/ -lc -lgcc #-L../libc/c.a
diff --git a/userland/sh/sh b/userland/sh/sh
new file mode 100755
index 0000000..7791495
--- /dev/null
+++ b/userland/sh/sh
Binary files differ
diff --git a/userland/sh/sh.c b/userland/sh/sh.c
new file mode 100644
index 0000000..5fe7f63
--- /dev/null
+++ b/userland/sh/sh.c
@@ -0,0 +1,224 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#define MAX_ARGS 20
+
+#define IS_SPECIAL(_c) (';' == _c || '\n' == _c || '&' == _c || '|' == _c)
+
+char *PATH = "/:/bin";
+
+int can_open(char *f) {
+ int rc;
+ if (-1 == (rc = open(f, O_RDONLY, 0)))
+ return 0;
+ close(rc);
+ return 1;
+}
+
+int find_path(char *file, char *buf) {
+ if ('/' == *file || '.' == *file) { // Real path
+ if (!can_open(file)) {
+ return 0;
+ }
+ strcpy(buf, file);
+ return 1;
+ }
+
+ char *p = PATH;
+ for (;;) {
+ char *b = p;
+ for (; *p && ':' != *p; p++)
+ ;
+ size_t l = p - b;
+ strlcpy(buf, b, l);
+ strcat(buf, "/");
+ strcat(buf, file);
+ if (can_open(buf)) {
+ return 1;
+ }
+ if (!*p)
+ break;
+ p++;
+ }
+ return 0;
+}
+
+int internal_exec(char *file, char **argv) {
+ char r_path[PATH_MAX];
+ if (!find_path(file, r_path))
+ return 0;
+ if (-1 == execv(r_path, argv)) {
+ perror("exec");
+ return 0;
+ }
+ return 1;
+}
+
+int execute_program(char *s, size_t l, int ignore_rc, int f_stdout,
+ int *new_out) {
+ if (!l)
+ return -1;
+
+ char c[l + 1];
+ memcpy(c, s, l);
+ c[l] = 0;
+
+ char *argv[MAX_ARGS];
+ int args = 0;
+ char *p = c;
+ for (size_t i = 0; i <= l; i++) {
+ if (' ' == c[i]) {
+ if (p == c + i) {
+ for (; ' ' == *p; p++, i++)
+ ;
+ i--;
+ continue;
+ }
+ c[i] = '\0';
+
+ if (strlen(p) == 0)
+ break;
+
+ argv[args] = p;
+ args++;
+ p = c + i + 1;
+ } else if (i == l) {
+ if (strlen(p) == 0)
+ break;
+ argv[args] = p;
+ args++;
+ }
+ }
+ argv[args] = NULL;
+
+ int fd[2];
+ pipe(fd);
+
+ int pid = fork();
+ if (0 == pid) {
+ if (new_out)
+ dup2(fd[1], 1);
+ if (-1 != f_stdout)
+ dup2(f_stdout, 0);
+ close(fd[0]);
+ if (!internal_exec(argv[0], argv)) {
+ printf("exec failed\n");
+ exit(1);
+ }
+ }
+
+ close(fd[1]);
+ if (new_out)
+ *new_out = fd[0];
+
+ if (ignore_rc)
+ return 0;
+
+ int rc;
+ wait(&rc);
+ return rc;
+}
+
+int compute_expression(char *exp) {
+ char *n = exp;
+ int ignore_next = 0;
+ int rc;
+ int f_stdout = -1;
+ int new_out = f_stdout;
+ for (; *exp; exp++) {
+ if (!IS_SPECIAL(*exp)) {
+ continue;
+ }
+ if ('\n' == *exp)
+ break;
+ if (';' == *exp) {
+ if (!ignore_next) {
+ execute_program(n, exp - n, 0, f_stdout, NULL);
+ }
+ n = exp + 1;
+ continue;
+ }
+ if ('&' == *exp && '&' == *(exp + 1)) {
+ if (!ignore_next) {
+ rc = execute_program(n, exp - n, 0, f_stdout, NULL);
+ if (0 != rc)
+ ignore_next = 1;
+ }
+ n = exp + 2;
+ exp++;
+ continue;
+ } else if ('&' == *exp) {
+ if (!ignore_next) {
+ execute_program(n, exp - n, 1, f_stdout, NULL);
+ }
+ n = exp + 1;
+ continue;
+ }
+ if ('|' == *exp && '|' == *(exp + 1)) {
+ if (!ignore_next) {
+ rc = execute_program(n, exp - n, 0, f_stdout, NULL);
+ if (0 == rc)
+ ignore_next = 1;
+ }
+ n = exp + 2;
+ exp++;
+ continue;
+ } else if ('|' == *exp) {
+ if (!ignore_next) {
+ execute_program(n, exp - n, 1, f_stdout, &new_out);
+ f_stdout = new_out;
+ }
+ n = exp + 1;
+ continue;
+ }
+ }
+ if (!ignore_next)
+ rc = execute_program(n, exp - n, 0, f_stdout, NULL);
+ return rc;
+}
+
+char *get_line(void) {
+ char *str = malloc(1024);
+ char *p = str;
+ int rc;
+ for (;;) {
+ if (0 == (rc = read(0, p, 1))) {
+ continue;
+ }
+ if (0 > rc) {
+ perror("read");
+ continue;
+ }
+ if (8 == *p) {
+ if (p == str)
+ continue;
+ putchar(*p);
+ p--;
+ continue;
+ }
+ putchar(*p);
+ if ('\n' == *p) {
+ break;
+ }
+ p++;
+ }
+ p++;
+ *p = '\0';
+ return str;
+}
+
+int main(void) {
+ for (;;) {
+ printf("/ : ");
+ char *l = get_line();
+ compute_expression(l);
+ free(l);
+ }
+ return 0;
+}
diff --git a/userland/sh/sh_a b/userland/sh/sh_a
new file mode 100755
index 0000000..7b435ab
--- /dev/null
+++ b/userland/sh/sh_a
Binary files differ
diff --git a/userland/sh/sh_bad b/userland/sh/sh_bad
new file mode 100755
index 0000000..7f93418
--- /dev/null
+++ b/userland/sh/sh_bad
Binary files differ
diff --git a/userland/snake/Makefile b/userland/snake/Makefile
new file mode 100644
index 0000000..f29df30
--- /dev/null
+++ b/userland/snake/Makefile
@@ -0,0 +1,15 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -static -fsanitize=shift,signed-integer-overflow,bounds
+LIB=-L../libgui -lgui -L../json -ljson -L../libc -lc -lgcc
+INC=-I../libc/ -I../json/ -I../libgui/
+BINS=snake
+all: $(BINS)
+
+snake.o: snake.c
+ $(CC) $(CFLAGS) $(INC) $(LIB) -o $@ -c $<
+
+snake: snake.o
+ $(CC) -nostdlib $(CFLAGS) -o $@ $^ $(LIB)
+
+clean:
+ rm $(BINS) *.o
diff --git a/userland/snake/snake.c b/userland/snake/snake.c
new file mode 100644
index 0000000..7bfb312
--- /dev/null
+++ b/userland/snake/snake.c
@@ -0,0 +1,109 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <json.h>
+#include <libgui.h>
+#include <poll.h>
+#include <pty.h>
+#include <socket.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+GUI_Window *global_w;
+
+#define SNAKE_WIDTH 10
+
+int get_key_event(char *c) {
+ if (0 >= read(global_w->ws_socket, c, 1))
+ return 0;
+ if (0 == *c)
+ return 0;
+ return 1;
+}
+
+void draw_snake(int x, int y) {
+ int sx = global_w->sx;
+ uint32_t *b = &global_w->bitmap_ptr[y * sx + x];
+ for (int i = 0; i < SNAKE_WIDTH; i++) {
+ for (int j = 0; j < SNAKE_WIDTH; j++) {
+ b[j] = 0xFF00FF;
+ }
+ b += sx;
+ }
+}
+
+int clamp(int *x, int *y) {
+ int clamped = 0;
+ if (*x + SNAKE_WIDTH > global_w->sx) {
+ *x = global_w->sx - SNAKE_WIDTH;
+ clamped = 1;
+ }
+ if (*y + SNAKE_WIDTH > global_w->sy) {
+ *y = global_w->sy - SNAKE_WIDTH;
+ clamped = 1;
+ }
+ return clamped;
+}
+
+void loop() {
+ int vel = SNAKE_WIDTH;
+ static int y_dir = SNAKE_WIDTH;
+ static int x_dir = 0;
+ static int snake_x = 10;
+ static int snake_y = 10;
+ static int ticks = 0;
+ ticks++;
+ char c;
+ if (get_key_event(&c)) {
+ switch (c) {
+ case 'w':
+ y_dir = -1 * vel;
+ x_dir = 0;
+ break;
+ case 's':
+ y_dir = vel;
+ x_dir = 0;
+ break;
+ case 'a':
+ x_dir = -1 * vel;
+ y_dir = 0;
+ break;
+ case 'd':
+ x_dir = vel;
+ y_dir = 0;
+ break;
+ case 'q':
+ exit(0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!(ticks > 5))
+ return;
+ ticks = 0;
+
+ snake_x += x_dir;
+ snake_y += y_dir;
+ if (clamp(&snake_x, &snake_y))
+ return;
+ draw_snake(snake_x, snake_y);
+ if (y_dir || x_dir)
+ GUI_UpdateWindow(global_w);
+}
+
+int main(void) {
+ global_w = GUI_CreateWindow(10, 10, 150 * 4, 150 * 4);
+ assert(global_w);
+ for (int i = 0; i < 150 * 150 * 4 * 4; i++)
+ global_w->bitmap_ptr[i] = 0xFFFFFF;
+ GUI_UpdateWindow(global_w);
+ for (;;) {
+ loop();
+ msleep(50);
+ }
+ return 0;
+}
diff --git a/userland/terminal/Makefile b/userland/terminal/Makefile
new file mode 100644
index 0000000..330f6e0
--- /dev/null
+++ b/userland/terminal/Makefile
@@ -0,0 +1,15 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS = -ggdb -ffreestanding -O0 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -static -fsanitize=shift,signed-integer-overflow,bounds
+LIB=-L../libgui -lgui -L../json -ljson -L../libc -lc -lgcc
+INC=-I../libc/ -I../json/ -I../libgui/
+BINS=term
+all: $(BINS)
+
+term.o: term.c
+ $(CC) $(CFLAGS) $(INC) $(LIB) -o $@ -c $<
+
+term: term.o
+ $(CC) -nostdlib $(CFLAGS) -o $@ $^ $(LIB)
+
+clean:
+ rm $(BINS) *.o
diff --git a/userland/terminal/term.c b/userland/terminal/term.c
new file mode 100644
index 0000000..8a2ae83
--- /dev/null
+++ b/userland/terminal/term.c
@@ -0,0 +1,166 @@
+#include <assert.h>
+#include <fcntl.h>
+#include <json.h>
+#include <libgui.h>
+#include <poll.h>
+#include <pty.h>
+#include <socket.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define TERM_BACKGROUND 0x000000
+
+int cmdfd;
+GUI_Window *global_w;
+uint32_t screen_pos_x = 0;
+uint32_t screen_pos_y = 0;
+int serial_fd;
+
+void execsh(void) {
+ char *argv[] = {NULL};
+ execv("/sh", argv);
+}
+
+uint32_t cursor_pos_x = 0;
+uint32_t cursor_pos_y = 0;
+
+void screen_remove_old_cursor() {
+ GUI_OverwriteFont(global_w, cursor_pos_x, cursor_pos_y, TERM_BACKGROUND);
+}
+
+void screen_update_cursor() {
+ GUI_OverwriteFont(global_w, screen_pos_x, screen_pos_y, 0xFFFFFF);
+ cursor_pos_x = screen_pos_x;
+ cursor_pos_y = screen_pos_y;
+}
+
+void screen_putchar(uint32_t c) {
+ if (c == '\n') {
+ screen_pos_x = 0;
+ screen_pos_y += 8;
+ screen_remove_old_cursor();
+ screen_update_cursor();
+ return;
+ }
+ if (screen_pos_y > global_w->sy - 8) {
+ uint8_t line[global_w->sx * sizeof(uint32_t)];
+ for (int i = 0; i < global_w->sy - 8; i++) {
+ memcpy(line, global_w->bitmap_ptr + ((i + 8) * global_w->sx),
+ global_w->sx * sizeof(uint32_t));
+ memcpy(global_w->bitmap_ptr + (i * global_w->sx), line,
+ global_w->sx * sizeof(uint32_t));
+ }
+ for (int i = 0; i < global_w->sx; i += 8) {
+ GUI_OverwriteFont(global_w, i, screen_pos_y - 8, TERM_BACKGROUND);
+ }
+ screen_pos_x = 0;
+ screen_pos_y -= 8;
+ }
+ GUI_DrawFont(global_w, screen_pos_x, screen_pos_y, c);
+ screen_pos_x += 8;
+ if (screen_pos_x >= global_w->sx - 8) {
+ screen_pos_x = 0;
+ screen_pos_y += 8;
+ }
+ screen_update_cursor();
+}
+
+void screen_delete_char(void) {
+ GUI_OverwriteFont(global_w, screen_pos_x, screen_pos_y, TERM_BACKGROUND);
+ screen_pos_x -= 8;
+ GUI_OverwriteFont(global_w, screen_pos_x, screen_pos_y, TERM_BACKGROUND);
+ screen_update_cursor();
+}
+
+void screen_print(const unsigned char *s, int l) {
+ for (; l; l--, s++) {
+ if ('\b' == *s) {
+ screen_delete_char();
+ continue;
+ }
+ screen_putchar((uint32_t)*s);
+ }
+}
+
+void newtty(void) {
+ int m;
+ int s;
+ openpty(&m, &s, NULL, NULL, NULL);
+ int pid = fork();
+ if (0 == pid) {
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ execsh();
+ return;
+ }
+ close(s);
+ cmdfd = m;
+}
+
+void writetty(const char *b, size_t len) {
+ write(serial_fd, b, len);
+ for (int rc; len; len -= rc) {
+ rc = write(cmdfd, b, len);
+ if (-1 == rc) {
+ perror("write");
+ assert(0);
+ }
+ }
+}
+
+void run() {
+ char buffer[4096];
+ struct pollfd fds[2];
+ fds[0].fd = cmdfd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ fds[1].fd = global_w->ws_socket;
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+ for (;; fds[0].revents = fds[1].revents = 0) {
+ poll(fds, 2, 0);
+ if (fds[0].revents & POLLIN) {
+ int rc;
+ if ((rc = read(cmdfd, buffer, 4096))) {
+ screen_print(buffer, rc);
+ GUI_UpdateWindow(global_w);
+ }
+ }
+ if (fds[1].revents & POLLIN) {
+ WS_EVENT e;
+ int rc;
+ if (0 >= (rc = read(global_w->ws_socket, &e, sizeof(e))))
+ continue;
+ if (1 == e.ev.release)
+ continue;
+ if (0 == e.ev.c)
+ continue;
+ write(cmdfd, &e.ev.c, 1);
+ }
+ }
+}
+
+int main(void) {
+ open("/dev/serial", O_RDWR, 0);
+ open("/dev/serial", O_RDWR, 0);
+ printf("running the terminal\n");
+ int pid = fork();
+ if (0 == pid) {
+ char *argv[] = {NULL};
+ execv("/ws", argv);
+ }
+
+ global_w = GUI_CreateWindow(20, 20, 250 * 4, 150 * 4);
+ assert(global_w);
+ for (int i = 0; i < 250 * 150 * 4 * 4; i++)
+ global_w->bitmap_ptr[i] = TERM_BACKGROUND;
+ // memset(global_w->bitmap_ptr, 0x0, 50 * 50);
+ GUI_UpdateWindow(global_w);
+ newtty();
+ run();
+ return 0;
+}
diff --git a/userland/test/Makefile b/userland/test/Makefile
new file mode 100644
index 0000000..7287939
--- /dev/null
+++ b/userland/test/Makefile
@@ -0,0 +1,16 @@
+#CC="/home/anton/opt/cross/bin/i686-elf-gcc"
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS = -O2 -Wall -Wextra -pedantic -Wimplicit-fallthrough -static
+LIB=-L../json -L../json/hashmap -ljson -lhashmap
+INC=-I../json/
+BINS=test
+all: $(BINS)
+
+test.o: test.c
+ $(CC) $(CFLAGS) $(INC) $(LIB) -o $@ -c $<
+
+test: test.o
+ $(CC) $(CFLAGS) -o $@ $^ $(LIB)
+
+clean:
+ rm test.o test
diff --git a/userland/test/linux.sh b/userland/test/linux.sh
new file mode 100755
index 0000000..d40d6cc
--- /dev/null
+++ b/userland/test/linux.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+gcc -DLINUX test.c -o ./local/a.out
+cd local
+./a.out
diff --git a/userland/test/local/a.out b/userland/test/local/a.out
new file mode 100755
index 0000000..1069f1f
--- /dev/null
+++ b/userland/test/local/a.out
Binary files differ
diff --git a/userland/test/local/testfile b/userland/test/local/testfile
new file mode 100644
index 0000000..629ae5e
--- /dev/null
+++ b/userland/test/local/testfile
@@ -0,0 +1 @@
+ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz
diff --git a/userland/test/test b/userland/test/test
new file mode 100755
index 0000000..63f9d8b
--- /dev/null
+++ b/userland/test/test
Binary files differ
diff --git a/userland/test/test.c b/userland/test/test.c
new file mode 100644
index 0000000..041b08a
--- /dev/null
+++ b/userland/test/test.c
@@ -0,0 +1,722 @@
+#include <assert.h>
+#include <fcntl.h>
+//#include <json.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if 1
+void dbgln(const char *fmt) { printf("%s\n", fmt); }
+#else
+void dbgln(const char *fmt, ...) {
+ printf("| ");
+ va_list ap;
+ va_start(ap, fmt);
+ int rc = vdprintf(1, fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+#endif
+
+void isx_test() {
+ dbgln("isx TEST");
+ {
+ assert(isspace(' '));
+ assert(isspace('\t'));
+ assert(!isspace('A'));
+
+ assert(isalpha('A'));
+ assert(!isalpha('9'));
+ assert(!isalpha(' '));
+ assert(!isalpha('z' + 1));
+
+ assert(isascii('A'));
+ assert(isascii('9'));
+ assert(isascii(' '));
+ assert(!isascii(0xFF));
+
+ assert(ispunct('.'));
+ assert(!ispunct('A'));
+
+ assert(isdigit('9'));
+ assert(!isdigit('A'));
+ }
+ dbgln("isx TEST PASSED");
+}
+
+void malloc_test(void) {
+ dbgln("malloc TEST");
+ for (int j = 0; j < 100; j++) {
+ // printf("j : %x\n", j);
+ uint8_t *t = malloc(400 + j);
+ memset(t, 0x43 + (j % 10), 400 + j);
+ uint8_t *p = malloc(900 + j);
+ memset(p, 0x13 + (j % 10), 900 + j);
+ assert(p > t && p > (t + 400 + j));
+
+ for (int i = 0; i < 400 + j; i++)
+ if (t[i] != 0x43 + (j % 10)) {
+ printf("t addy: %x, val: %x, should be: %x\n", t + i, t[i],
+ 0x43 + (j % 10));
+ assert(0);
+ }
+
+ for (int i = 0; i < 900 + j; i++)
+ if (p[i] != 0x13 + (j % 10)) {
+ printf("p addy: %x, val: %x, should be: %x\n", p + i, t[i],
+ 0x13 + (j % 10));
+ assert(0);
+ }
+ free(t);
+ }
+ dbgln("malloc TEST PASSED");
+}
+
+void memcmp_test(void) {
+ dbgln("memcmp TEST");
+ {
+ assert(0 == memcmp("\xAA\xBB\xCC", "\xAA\xBB\xCC", 3));
+ assert(1 == memcmp("\xAF\xBB\xCC", "\xAA\xBB\xCC", 3));
+ }
+ dbgln("memcmp TEST PASSED");
+}
+
+void memcpy_test(void) {
+ dbgln("memcpy TEST");
+ {
+ char buf[100];
+ assert(buf == memcpy(buf, "\xAA\xBB\xCC", 3));
+ assert(0 == memcmp(buf, "\xAA\xBB\xCC", 3));
+ assert(buf == memcpy(buf, "\xCC\xBB\xCC", 3));
+ assert(0 == memcmp(buf, "\xCC\xBB\xCC", 3));
+
+ assert(buf == memcpy(buf, "\xAA\xBB\xCC\xDD", 4));
+ assert(0 == memcmp(buf, "\xAA\xBB\xCC\xDD", 4));
+ assert(buf == memcpy(buf, "\xAA\xBB\xCC\xEE", 3));
+ assert(0 == memcmp(buf, "\xAA\xBB\xCC\xDD", 4));
+ }
+ dbgln("memcpy TEST PASSED");
+}
+
+void memmove_test(void) {
+ dbgln("memmove TEST");
+ {
+ char buf[100];
+ memcpy(buf, "foobar", 6);
+ const char *p = buf + 2; // obar
+
+ assert(buf == memmove(buf, p, 4));
+ assert(0 == memcmp(buf, "obarar", 6));
+ }
+ dbgln("memmove TEST PASSED");
+}
+
+void strlen_test(void) {
+ dbgln("strlen TEST");
+ {
+ assert(0 == strlen(""));
+ assert(3 == strlen("%is"));
+ assert(3 == strlen("foo"));
+ assert(3 == strlen("bar"));
+ assert(6 == strlen("foobar"));
+ }
+ dbgln("strlen TEST PASSED");
+}
+
+void strnlen_test(void) {
+ dbgln("strnlen TEST");
+ {
+ assert(0 == strnlen("", 0));
+ assert(0 == strnlen("", 3));
+ assert(3 == strnlen("foo", 3));
+ assert(2 == strnlen("bar", 2));
+ assert(3 == strnlen("foobar", 3));
+ assert(6 == strnlen("foobar", 6));
+ assert(6 == strnlen("foobar", 8));
+ }
+ dbgln("strnlen TEST PASSED");
+}
+
+/*
+void json_test(void) {
+ dbgln("JSON TEST");
+ {
+ const char *s = "{\"string\": \"hello\", \"object\": {\"string\": \"ff\"}}";
+ JSON_CTX ctx;
+ JSON_Init(&ctx);
+ JSON_Parse(&ctx, s);
+ JSON_ENTRY *main_entry = JSON_at(&ctx.global_object, 0);
+ assert(main_entry != NULL);
+ JSON_ENTRY *index_entry = JSON_at(main_entry, 0);
+ JSON_ENTRY *string_entry = JSON_search_name(main_entry, "string");
+ assert(index_entry != NULL);
+ assert(index_entry == string_entry);
+ char *str = index_entry->value;
+ assert(0 == strcmp(str, "hello"));
+
+ JSON_ENTRY *object = JSON_search_name(main_entry, "object");
+ assert(object != NULL);
+ JSON_ENTRY *object_string = JSON_search_name(object, "string");
+ assert(object_string != NULL);
+ assert(0 == strcmp(object_string->value, "ff"));
+ }
+ dbgln("JSON TEST PASSED");
+}*/
+
+void random_test(void) {
+ dbgln("/dev/random TEST");
+ {
+ int fd = open("/dev/random", O_RDONLY, 0);
+ uint8_t buffer_1[256];
+ assert(256 == read(fd, buffer_1, 256));
+ close(fd);
+ fd = open("/dev/random", O_RDONLY, 0);
+ uint8_t buffer_2[256];
+ assert(256 == read(fd, buffer_2, 256));
+ close(fd);
+ assert(0 != memcmp(buffer_1, buffer_2, 256));
+ }
+ dbgln("/dev/random TEST PASSED");
+}
+
+void sscanf_test(void) {
+ dbgln("sscanf TEST");
+ {
+ int d1;
+ int d2;
+ sscanf("1337:7331", "%d:%d", &d1, &d2);
+ assert(1337 == d1);
+ assert(7331 == d2);
+ sscanf(" 1234 31415", "%d%d", &d1, &d2);
+ assert(1234 == d1);
+ assert(31415 == d2);
+ sscanf(" 1234 441122", "%*d%d", &d1);
+ assert(441122 == d1);
+
+ char f;
+ char b;
+ sscanf("f:b", "%c:%c", &f, &b);
+ assert('f' == f);
+ assert('b' == b);
+ }
+ dbgln("sscanf TEST PASSED");
+}
+
+void strtoul_test(void) {
+ dbgln("strtoul TEST");
+ {
+ long r;
+ char *s = "1234";
+ char *endptr;
+ r = strtoul("1234", &endptr, 10);
+ assert(1234 == r);
+ assert(endptr == (s + 4));
+ }
+ dbgln("strtoul TEST PASSED");
+}
+
+void strcspn_test(void) {
+ dbgln("strcspn TEST");
+ {
+ assert(4 == strcspn("1234", ""));
+ assert(3 == strcspn("1234", "4"));
+ assert(2 == strcspn("1234", "43"));
+ assert(0 == strcspn("1234", "2143"));
+ }
+ dbgln("strcspn TEST PASSED");
+}
+
+void strpbrk_test(void) {
+ dbgln("strpbrk TEST");
+ {
+ char *s = "01234";
+ assert(s + 3 == strpbrk(s, "3"));
+ assert(s + 4 == strpbrk(s, "4"));
+ assert(s + 2 == strpbrk(s, "2"));
+ assert(s + 2 == strpbrk(s, "23"));
+ assert(NULL == strpbrk(s, "5"));
+ }
+ dbgln("strpbrk TEST PASSED");
+}
+
+void strcpy_test(void) {
+ dbgln("strcpy TEST");
+ {
+ char buf[10];
+ memset(buf, '\xBB', 10);
+ strcpy(buf, "hello");
+ assert(0 == memcmp(buf, "hello\0\xBB\xBB\xBB\xBB", 10));
+ }
+ dbgln("strcpy TEST PASSED");
+}
+
+void strncpy_test(void) {
+ dbgln("strncpy TEST");
+ {
+ char buf[10];
+ memset(buf, '\xBB', 10);
+ strncpy(buf, "hello", 3);
+ assert(0 == memcmp(buf, "hel\xBB\xBB\xBB\xBB\xBB\xBB\xBB", 10));
+ strncpy(buf, "hello", 6);
+ assert(0 == memcmp(buf, "hello\0\xBB\xBB\xBB\xBB", 10));
+ }
+ dbgln("strncpy TEST PASSED");
+}
+
+void strlcpy_test(void) {
+ dbgln("strlcpy TEST");
+ {
+ char buf[10];
+ memset(buf, '\xBB', 10);
+ strlcpy(buf, "hello", 3);
+ assert(0 == memcmp(buf, "hel\x00\xBB\xBB\xBB\xBB\xBB\xBB", 10));
+ }
+ dbgln("strlcpy TEST PASSED");
+}
+
+void strcat_test(void) {
+ dbgln("strcat TEST");
+ {
+ char buf[10];
+ memset(buf, '\xBB', 10);
+ strcpy(buf, "hel");
+ assert(0 == memcmp(buf, "hel\0\xBB\xBB\xBB\xBB\xBB\xBB", 10));
+ strcat(buf, "lo");
+ assert(0 == memcmp(buf, "hello\0\xBB\xBB\xBB\xBB", 10));
+ }
+ dbgln("strcat TEST PASSED");
+}
+
+void strchr_test(void) {
+ dbgln("strchr TEST");
+ {
+ char *s = "0123412345";
+ assert(s + 3 == strchr(s, '3'));
+ assert(s + 4 + 5 == strchr(s, '5'));
+ assert(s + 4 == strchr(s, '4'));
+ assert(NULL == strchr(s, '9'));
+ }
+ dbgln("strchr TEST PASSED");
+}
+
+void strrchr_test(void) {
+ dbgln("strrchr TEST");
+ {
+ char *s = "0123412345";
+ assert(s + 3 + 4 == strrchr(s, '3'));
+ assert(s == strrchr(s, '0'));
+ assert(s + 4 + 5 == strrchr(s, '5'));
+ assert(NULL == strrchr(s, '9'));
+ }
+ dbgln("strrchr TEST PASSED");
+}
+
+void strdup_test(void) {
+ dbgln("strdup TEST");
+ {
+ char *s;
+ s = strdup("hello");
+ assert(s);
+ assert(0 == strcmp(s, "hello"));
+ free(s);
+
+ s = strdup("abc");
+ assert(s);
+ assert(0 == strcmp(s, "abc"));
+ free(s);
+ }
+ dbgln("strdup TEST PASSED");
+}
+
+void strndup_test(void) {
+ dbgln("strdup TEST");
+ {
+ char *s;
+ s = strndup("hello", 5);
+ assert(s);
+ assert(0 == strcmp(s, "hello"));
+ free(s);
+
+ s = strndup("hello", 3);
+ assert(s);
+ assert(0 == strcmp(s, "hel"));
+ free(s);
+ }
+ dbgln("strndup TEST PASSED");
+}
+
+void abs_test(void) {
+ dbgln("abs TEST");
+ {
+ assert(1 == abs(1));
+ assert(1 == abs(-1));
+ assert(0 == abs(0));
+ }
+ dbgln("abs TEST PASSED");
+}
+
+void strspn_test(void) {
+ dbgln("strspn TEST");
+ {
+ assert(0 == strspn("abc", ""));
+ assert(0 == strspn("abc", "xyz"));
+ assert(1 == strspn("abc", "a"));
+ assert(2 == strspn("abc", "ab"));
+ assert(3 == strspn("abcd", "abc"));
+ assert(3 == strspn("abcde", "abce"));
+ }
+ dbgln("strspn TEST PASSED");
+}
+
+void strtol_test(void) {
+ dbgln("strtol TEST");
+ {
+ char *s = "1234";
+ char *e;
+ long r;
+ assert(1234 == strtol(s, &e, 10));
+ assert(e == (s + 4));
+ }
+ dbgln("strtol TEST PASSED");
+}
+
+void strcmp_test(void) {
+#define EQ(_s1) \
+ { assert(0 == strcmp(_s1, _s1)); }
+ dbgln("strcmp TEST");
+ {
+ EQ("hello, world");
+ EQ("");
+ EQ("int: 100, hex: 64, octal: 144 a");
+ }
+ dbgln("strcmp TEST PASSED");
+}
+
+void strncmp_test(void) {
+ dbgln("strncmp TEST");
+ {
+ assert(0 == strncmp("hello", "hello", 5));
+ assert(0 < strncmp("foo", "bar", 3));
+ assert(0 == strncmp("foobar", "foofoo", 3));
+ }
+ dbgln("strncmp TEST PASSED");
+}
+
+void strcasecmp_test(void) {
+ dbgln("strcasecmp TEST");
+ {
+ assert(0 == strcasecmp("foobar", "FOObar"));
+ assert(0 == strcasecmp("foobar", "foobar"));
+ assert(6 == strcasecmp("foobar", "bar"));
+ }
+ dbgln("strcasecmp TEST PASSED");
+}
+
+void strncasecmp_test(void) {
+ dbgln("strncasecmp TEST");
+ {
+ assert(0 == strncasecmp("foobar", "FOObar", 6));
+ assert(0 == strncasecmp("foobar", "foobar", 6));
+ assert(0 == strncasecmp("foo", "foobar", 3));
+ assert(-1 == strncasecmp("foo", "foobar", 4));
+ }
+ dbgln("strncasecmp TEST PASSED");
+}
+
+void strstr_test(void) {
+ dbgln("strstr TEST");
+ {
+ const char *a = "foobar123";
+ const char *b = "bar";
+ assert(a + 3 == strstr(a, b));
+ }
+ dbgln("strstr TEST PASSED");
+}
+
+void strtok_test(void) {
+ dbgln("strtok TEST");
+ {
+ char *s = "hello,world,goodbye";
+ assert(0 == strcmp(strtok(s, ","), "hello"));
+ assert(0 == strcmp(strtok(NULL, ","), "world"));
+ assert(0 == strcmp(strtok(NULL, ","), "goodbye"));
+ assert(NULL == strtok(NULL, ","));
+ char *s2 = "hello,,world,,goodbye,test";
+ assert(0 == strcmp(strtok(s2, ",,"), "hello"));
+ assert(0 == strcmp(strtok(NULL, ",,"), "world"));
+ assert(0 == strcmp(strtok(NULL, ","), "goodbye"));
+ assert(0 == strcmp(strtok(NULL, ","), "test"));
+ assert(NULL == strtok(NULL, ","));
+ }
+ dbgln("strtok TEST PASSED");
+}
+
+void fseek_test(void) {
+ dbgln("fseek TEST");
+ {
+#ifndef LINUX
+ FILE *fp = fopen("/testfile", "r");
+#else
+ FILE *fp = fopen("testfile", "r");
+#endif
+ assert(NULL != fp);
+ char c;
+ assert(0 == ftell(fp));
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('A' == c);
+
+ assert(1 == ftell(fp));
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('B' == c);
+
+ assert(2 == ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ assert(0 == ftell(fp));
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('A' == c);
+ fseek(fp, 2, SEEK_SET);
+ assert(2 == ftell(fp));
+
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('C' == c);
+ fseek(fp, 2, SEEK_CUR);
+ assert(5 == ftell(fp));
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('F' == c);
+
+ char buffer[100];
+ assert(6 == ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ assert(1 == fread(buffer, 10, 1, fp));
+ assert(0 == memcmp(buffer, "ABCDEFGHIJK", 10));
+ assert(10 == ftell(fp));
+ fseek(fp, 1, SEEK_SET);
+ assert(10 == fread(buffer, 1, 10, fp));
+ assert(0 == memcmp(buffer, "BCDEFGHIJKL", 10));
+
+ fseek(fp, 0, SEEK_END);
+ assert(0 == fread(&c, 1, 1, fp));
+ fseek(fp, -2, SEEK_CUR);
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('z' == c);
+ fseek(fp, -3, SEEK_END);
+ assert(1 == fread(&c, 1, 1, fp));
+ assert('y' == c);
+
+ assert(0 == fclose(fp));
+ }
+ dbgln("fseek TEST PASSED");
+}
+
+void printf_test(void) {
+#define EXP(exp) \
+ { \
+ int exp_n = strlen(exp); \
+ if (0 != strcmp(buf, exp) || buf_n != exp_n) { \
+ printf("buf n : %d\n", buf_n); \
+ printf("exp n : %d\n", snprintf(NULL, 0, "%s", exp)); \
+ printf("Expected: %s", exp); \
+ printf("\n"); \
+ printf("Got: %s", buf); \
+ printf("\n"); \
+ assert(0); \
+ } \
+ }
+ dbgln("printf TEST");
+ {
+ char buf[200];
+ int buf_n;
+ buf_n = sprintf(buf, "hello");
+ EXP("hello");
+ buf_n = sprintf(buf, "hello %s\n", "world");
+ EXP("hello world\n");
+ buf_n = sprintf(buf, "int: %d", 100);
+ EXP("int: 100");
+ buf_n = sprintf(buf, "int: %d, hex: %x, octal: %o a", 100, 100, 100);
+ EXP("int: 100, hex: 64, octal: 144 a");
+
+ buf_n = sprintf(buf, "int: %02d", 1);
+ EXP("int: 01");
+ buf_n = sprintf(buf, "int: %2d", 1);
+ EXP("int: 1");
+ buf_n = sprintf(buf, "int: %00d", 1);
+ EXP("int: 1");
+
+ buf_n = sprintf(buf, "hex: %02x", 1);
+ EXP("hex: 01");
+ buf_n = sprintf(buf, "hex: %2x", 1);
+ EXP("hex: 1");
+ buf_n = sprintf(buf, "hex: %00x", 1);
+ EXP("hex: 1");
+ buf_n = sprintf(buf, "hex: %x", 0x622933f2);
+ EXP("hex: 622933f2");
+
+ buf_n = sprintf(buf, "oct: %02o", 1);
+ EXP("oct: 01");
+ buf_n = sprintf(buf, "oct: %2o", 1);
+ EXP("oct: 1");
+ buf_n = sprintf(buf, "oct: %00o", 1);
+ EXP("oct: 1");
+
+ buf_n = sprintf(buf, "int: %-2dend", 1);
+ EXP("int: 1 end");
+ buf_n = sprintf(buf, "int: %-2dend", 12);
+ EXP("int: 12end");
+ buf_n = sprintf(buf, "int: %-0dend", 1);
+ EXP("int: 1end");
+
+ buf_n = sprintf(buf, "int: %-2xend", 1);
+ EXP("int: 1 end");
+ buf_n = sprintf(buf, "int: %-2xend", 12);
+ EXP("int: c end");
+ buf_n = sprintf(buf, "int: %-0xend", 1);
+ EXP("int: 1end");
+
+ buf_n = sprintf(buf, "int: %-2oend", 1);
+ EXP("int: 1 end");
+ buf_n = sprintf(buf, "int: %-2oend", 12);
+ EXP("int: 14end");
+ buf_n = sprintf(buf, "int: %-0oend", 1);
+ EXP("int: 1end");
+
+ buf_n = sprintf(buf, "string: %.3s", "foobar");
+ EXP("string: foo");
+ buf_n = sprintf(buf, "string: %.4s", "foobar");
+ EXP("string: foob");
+
+ buf_n = sprintf(buf, "string: %s\n", "ABCDEFGHIJKLMNOPQRSTUVXYZ");
+ EXP("string: ABCDEFGHIJKLMNOPQRSTUVXYZ\n");
+
+ buf_n = sprintf(buf, "string: %6send", "hello");
+ EXP("string: helloend");
+ buf_n = sprintf(buf, "string: %-6send", "hello");
+ EXP("string: hello end");
+ const char *data = "hello";
+ char *foo = "foo";
+ buf_n = sprintf(buf, " %-20s Legacy alias for \"%s\"\n", data, foo);
+ EXP(" hello Legacy alias for \"foo\"\n");
+ buf_n = sprintf(buf, " %-20s %s%s\n", data, foo, "bar");
+ EXP(" hello foobar\n");
+ // Make sure that parameters have not changed
+ assert(0 == strcmp(data, "hello"));
+ assert(0 == strcmp(foo, "foo"));
+
+ /* snprintf test */
+ char out_buffer[100];
+ memset(out_buffer, 'A', 100);
+ assert(5 == snprintf(out_buffer, 5, "hello"));
+ memcmp("helloAAAAA", out_buffer, 10);
+ assert(10 == snprintf(out_buffer, 5, "hellofoooo"));
+ memcmp("helloAAAAA", out_buffer, 10);
+ assert(10 == snprintf(out_buffer, 10, "hellofoooo"));
+ memcmp("hellofoookAA", out_buffer, 12);
+ }
+ dbgln("printf TEST PASSED");
+}
+
+int cmpfunc(const void *a, const void *b) { return (*(char *)a - *(char *)b); }
+
+void qsort_test(void) {
+ dbgln("qsort TEST");
+ {
+ char buf[] = {'B', 'D', 'C', 'A', '\0'};
+ qsort(buf, 4, sizeof(char), cmpfunc);
+ assert(0 == memcmp(buf, "ABCD", 4));
+ }
+ dbgln("qsort TEST PASSED");
+}
+
+void basename_test(void) {
+ dbgln("basename TEST");
+ {
+ char path[100];
+ const char *s;
+ strcpy(path, "/foo/bar");
+ s = basename(path);
+ assert(0 == strcmp(s, "bar"));
+
+ strcpy(path, "/foo/bar/");
+ s = basename(path);
+ assert(0 == strcmp(s, "bar"));
+
+ strcpy(path, "/");
+ s = basename(path);
+ assert(0 == strcmp(s, "/"));
+
+ strcpy(path, "//");
+ s = basename(path);
+ assert(0 == strcmp(s, "/"));
+
+ strcpy(path, "");
+ s = basename(path);
+ assert(0 == strcmp(s, "."));
+
+ s = basename(NULL);
+ assert(0 == strcmp(s, "."));
+ }
+ dbgln("basename TEST PASSED");
+}
+
+void dirname_test(void) {
+ dbgln("dirname TEST");
+ {
+ char path[100];
+ const char *s;
+ strcpy(path, "/foo/bar");
+ s = dirname(path);
+ assert(0 == strcmp(s, "foo"));
+
+ strcpy(path, "/foo/bar/");
+ s = dirname(path);
+ assert(0 == strcmp(s, "foo"));
+
+ strcpy(path, "/foo/bar/zoo");
+ s = dirname(path);
+ assert(0 == strcmp(s, "bar"));
+ }
+ dbgln("dirname TEST PASSED");
+}
+
+int main(void) {
+ dbgln("START");
+ malloc_test();
+ // json_test();
+ // random_test();
+ isx_test();
+ memcmp_test();
+ memcpy_test();
+ memmove_test();
+ strlen_test();
+ strnlen_test();
+ sscanf_test();
+ strtoul_test();
+ strcspn_test();
+ strpbrk_test();
+ strcpy_test();
+ strncpy_test();
+ strlcpy_test();
+ strcat_test();
+ strchr_test();
+ strrchr_test();
+ strdup_test();
+ strndup_test();
+ strspn_test();
+ strtol_test();
+ strcmp_test();
+ strncmp_test();
+ strcasecmp_test();
+ strncasecmp_test();
+ strstr_test();
+ strtok_test();
+ abs_test();
+ // fseek_test();
+ printf_test();
+ qsort_test();
+ basename_test();
+ dirname_test();
+ // TODO: Add mkstemp
+ return 0;
+}
diff --git a/userland/windowserver/Makefile b/userland/windowserver/Makefile
new file mode 100644
index 0000000..f67bd66
--- /dev/null
+++ b/userland/windowserver/Makefile
@@ -0,0 +1,16 @@
+CC="/home/anton/prj/osdev/sysroot/bin/i686-sb-gcc"
+CFLAGS = -ggdb -ffreestanding -O2 -Wall -Wextra -pedantic -mgeneral-regs-only -Wimplicit-fallthrough -fsanitize=shift,signed-integer-overflow,bounds
+BIN=ws
+LIB=-L../json -ljson -L../json/hashmap -lhashmap -L../libc -lc -lgcc
+INC=-I../json/ -I../libgui/
+all: $(BIN)
+OBJ=ws.o draw.o
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(INC) $(LIB) -o $@ -c $<
+
+clean:
+ rm $(OBJ) ws
+
+$(BIN): $(OBJ)
+ $(CC) -o $(BIN) -ffreestanding -nostdlib $(CFLAGS) $(OBJ) $(LIB)
diff --git a/userland/windowserver/draw.c b/userland/windowserver/draw.c
new file mode 100644
index 0000000..caa16a2
--- /dev/null
+++ b/userland/windowserver/draw.c
@@ -0,0 +1,80 @@
+#include "draw.h"
+#include <string.h>
+
+#define BPP 4
+
+#define place_pixel(_p, _w, _h) \
+ { *(uint32_t *)(disp->back_buffer + BPP * (_w) + (WIDTH * BPP * (_h))) = _p; }
+
+#define place_pixel_pos(_p, _pos) \
+ { *(uint32_t *)(disp->back_buffer + BPP * (_pos)) = _p; }
+
+int mx;
+int my;
+
+void update_display(DISPLAY *disp) {
+ for (int i = 0; i < 20; i++) {
+ place_pixel(0xFFFFFFFF, mx + i, my + i);
+ place_pixel(0xFFFFFFFF, mx, my + i / 2);
+ place_pixel(0xFFFFFFFF, mx + i / 2, my);
+ }
+ for (int i = 0; i < disp->size; i++)
+ disp->true_buffer[i] = disp->back_buffer[i];
+ // memcpy(disp->true_buffer, disp->back_buffer, disp->size);
+}
+
+void draw_wallpaper(DISPLAY *disp) {
+ for (int i = 0; i < disp->size / BPP; i++) {
+ place_pixel_pos(0xFF00FF, i);
+ }
+}
+
+void draw_mouse(DISPLAY *disp, int mouse_x, int mouse_y) {
+ mx = mouse_x;
+ my = mouse_y;
+ update_full_display(disp, mouse_x, mouse_y);
+}
+
+void draw_window(DISPLAY *disp, const WINDOW *w) {
+ int x, y;
+ uint8_t border_size = disp->border_size;
+ const int px = w->x + border_size;
+ const int py = w->y;
+ const int sx = w->sx;
+ const int sy = w->sy;
+ x = px;
+ y = py;
+ for (int i = 0; i < sy; i++) {
+ if((i+py)*WIDTH + px > HEIGHT*WIDTH)
+ break;
+ uint32_t *ptr = disp->back_buffer + BPP * ((i + py) * WIDTH) + px * BPP;
+ if(i*sx > HEIGHT*WIDTH)
+ break;
+ uint32_t *bm = &w->bitmap_ptr[i * sx];
+ // ((uint8_t *)w->bitmap_ptr) + BPP * ((i + py) * WIDTH) + px * BPP;
+ for (int j = 0; j < sx; j++) {
+ ptr[j] = bm[j];
+ }
+ }
+}
+
+void update_active_window(DISPLAY *disp) {
+ for (int i = 0; i < 100; i++) {
+ if (!disp->clients[i])
+ continue;
+ if (!disp->clients[i]->w)
+ continue;
+ draw_window(disp, disp->clients[i]->w);
+ }
+}
+
+void update_full_display(DISPLAY *disp, int mouse_x, int mouse_y) {
+ draw_wallpaper(disp); /*
+ for (int i = 0; i < 100; i++) {
+ if (!disp->windows[i])
+ continue;
+ draw_window(disp, disp->windows[i]);
+ }*/
+ update_active_window(disp);
+ update_display(disp);
+}
diff --git a/userland/windowserver/draw.h b/userland/windowserver/draw.h
new file mode 100644
index 0000000..bf9ff4f
--- /dev/null
+++ b/userland/windowserver/draw.h
@@ -0,0 +1,13 @@
+#ifndef DRAW_H
+#define DRAW_H
+#include "ws.h"
+
+#define WIDTH 0x500
+#define HEIGHT 0x320
+
+void draw_wallpaper(DISPLAY *disp);
+void draw_window(DISPLAY *disp, const WINDOW *w);
+void update_full_display(DISPLAY *disp, int mouse_x, int mouse_y);
+void update_active_window(DISPLAY *disp);
+void draw_mouse(DISPLAY *disp, int mouse_x, int mouse_y);
+#endif
diff --git a/userland/windowserver/font.h b/userland/windowserver/font.h
new file mode 100644
index 0000000..ad63e41
--- /dev/null
+++ b/userland/windowserver/font.h
@@ -0,0 +1,133 @@
+#ifndef FONT_H
+#define FONT_H
+char font8x8_basic[128][8] = {
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
+ { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
+ { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
+ { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
+ { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
+ { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
+ { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
+ { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
+ { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
+ { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
+ { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
+ { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
+ { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
+ { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
+ { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
+ { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
+ { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
+ { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
+ { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
+ { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
+ { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
+ { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
+ { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
+ { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
+ { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
+ { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;)
+ { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
+ { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
+ { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
+ { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
+ { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
+ { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
+ { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
+ { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
+ { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
+ { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
+ { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
+ { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
+ { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
+ { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
+ { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
+ { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
+ { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
+ { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
+ { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
+ { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
+ { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
+ { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
+ { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
+ { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
+ { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
+ { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
+ { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
+ { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
+ { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
+ { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
+ { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
+ { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
+ { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
+ { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
+ { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
+ { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
+ { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
+ { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
+ { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
+ { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
+ { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
+ { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
+ { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
+ { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
+ { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
+ { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
+ { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
+ { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
+ { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
+ { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
+ { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
+ { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
+ { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
+ { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
+ { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
+ { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
+ { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
+ { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
+ { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
+ { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
+ { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
+ { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
+ { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
+ { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
+ { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
+ { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
+};
+#endif
diff --git a/userland/windowserver/ws.c b/userland/windowserver/ws.c
new file mode 100644
index 0000000..e4f1a9e
--- /dev/null
+++ b/userland/windowserver/ws.c
@@ -0,0 +1,375 @@
+#include <fcntl.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdint.h>
+//#include <sys/mman.h>
+#include "draw.h"
+#include "font.h"
+#include "ws.h"
+#include <assert.h>
+#include <socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define WINDOW_SERVER_SOCKET "/windowserver"
+
+CLIENT *clients[100];
+CLIENT *active_client;
+
+int mouse_x = 0;
+int mouse_y = 0;
+
+// void *true_display;
+// void *buffer_display;
+// uint64_t display_size;
+// int border_size;
+// uint8_t border_color;
+DISPLAY main_display;
+
+struct pollfd *fds;
+uint64_t num_fds;
+
+uint64_t socket_fd_poll;
+uint64_t keyboard_fd_poll;
+uint64_t mouse_fd_poll;
+
+// Taken from drivers/keyboard.c
+struct KEY_EVENT {
+ char c;
+ uint8_t mode; // (shift (0 bit)) (alt (1 bit))
+ uint8_t release; // 0 pressed, 1 released
+};
+
+int create_socket(void) {
+ struct sockaddr_un address;
+ int fd = socket(AF_UNIX, 0, 0);
+ address.sun_family = AF_UNIX;
+ size_t str_len = strlen(WINDOW_SERVER_SOCKET);
+ address.sun_path = malloc(str_len + 1);
+ memcpy(address.sun_path, WINDOW_SERVER_SOCKET, str_len);
+ address.sun_path[str_len] = 0;
+
+ bind(fd, (struct sockaddr *)(&address), sizeof(address));
+ // for(;;);
+ /*free(address.sun_path);*/
+ return fd;
+}
+
+void setup_display(DISPLAY *disp, const char *path, uint64_t size) {
+ disp->wallpaper_color = 0x3;
+ disp->size = size;
+ disp->vga_fd = open(path, O_RDWR, 0);
+ if (-1 == disp->vga_fd) {
+ perror("open");
+ for (;;)
+ ;
+ }
+ disp->true_buffer = mmap(NULL, size, 0, 0, disp->vga_fd, 0);
+ disp->back_buffer = malloc(size + 0x1000);
+ disp->clients = clients;
+}
+
+void setup(void) {
+ setup_display(&main_display, "/dev/vbe", 0xBB8000);
+ draw_wallpaper(&main_display);
+ update_display(&main_display);
+
+ main_display.border_size = 1;
+ main_display.border_color = 0xF;
+ active_client = NULL;
+ for (int i = 0; i < 100; i++) {
+ clients[i] = NULL;
+ }
+
+ num_fds = 100;
+ fds = malloc(sizeof(struct pollfd[num_fds]));
+ memset(fds, 0, sizeof(struct pollfd[num_fds]));
+ for (size_t i = 0; i < num_fds; i++)
+ fds[i].fd = -1;
+ // Create socket
+ int socket_fd = create_socket();
+ assert(socket_fd >= 0);
+ socket_fd_poll = 0;
+ fds[socket_fd_poll].fd = socket_fd;
+ fds[socket_fd_poll].events = POLLIN;
+ fds[socket_fd_poll].revents = 0;
+ int keyboard_fd = open("/dev/keyboard", O_RDONLY | O_NONBLOCK, 0);
+ assert(keyboard_fd >= 0);
+ keyboard_fd_poll = 1;
+ fds[keyboard_fd_poll].fd = keyboard_fd;
+ fds[keyboard_fd_poll].events = POLLIN;
+ fds[keyboard_fd_poll].revents = 0;
+ int mouse_fd = open("/dev/mouse", O_RDONLY, 0);
+ assert(mouse_fd >= 0);
+ mouse_fd_poll = 2;
+ fds[mouse_fd_poll].fd = mouse_fd;
+ fds[mouse_fd_poll].events = POLLIN;
+ fds[mouse_fd_poll].revents = 0;
+}
+
+void reset_revents(struct pollfd *fds, size_t s) {
+ for (size_t i = 0; i < s - 1; i++)
+ fds[i].revents = 0;
+}
+
+void add_fd(int fd) {
+ int i;
+ for (i = 0; i < num_fds; i++)
+ if (-1 == fds[i].fd)
+ break;
+
+ fds[i].fd = fd;
+ fds[i].events = POLLIN | POLLHUP;
+ // fds[i].events = POLLIN;
+ fds[i].revents = 0;
+}
+
+void add_client(int fd) {
+ int client_socket = accept(fd, NULL, NULL);
+ add_fd(client_socket);
+ int i;
+ for (i = 0; i < 100; i++)
+ if (!clients[i])
+ break;
+ printf("adding client: %d\n", i);
+ CLIENT *c = clients[i] = malloc(sizeof(CLIENT));
+ c->fd = client_socket;
+ active_client = c;
+ printf("clients[0]: %x\n", clients[0]);
+}
+
+#define CLIENT_EVENT_CREATESCREEN 0
+#define CLIENT_EVENT_UPDATESCREEN 1
+
+void add_window(CLIENT *c, int fd, int x, int y, int sx, int sy) {
+ WINDOW *w = malloc(sizeof(WINDOW));
+ w->bitmap_fd = fd;
+ w->bitmap_ptr = mmap(NULL, sx * sy * sizeof(uint32_t), 0, 0, fd, 0);
+ w->x = x;
+ w->y = y;
+ w->sx = sx;
+ w->sy = sy;
+ c->w = w;
+}
+
+typedef struct {
+ uint16_t px;
+ uint16_t py;
+ uint16_t sx;
+ uint16_t sy;
+ uint8_t name_len;
+} WS_EVENT_CREATE;
+
+void parse_client_event(CLIENT *c) {
+ uint8_t event_type;
+ if (0 == read(c->fd, &event_type, sizeof(uint8_t))) {
+ printf("empty\n");
+ return;
+ }
+ if (0 == event_type) {
+ update_full_display(&main_display, mouse_x, mouse_y);
+ return;
+ }
+ if (1 == event_type) {
+ WS_EVENT_CREATE e;
+ for (; 0 == read(c->fd, &e, sizeof(e));)
+ ;
+ uint8_t bitmap_name[e.name_len + 1];
+ read(c->fd, bitmap_name, e.name_len);
+ bitmap_name[e.name_len] = '\0';
+ int bitmap_fd = shm_open(bitmap_name, O_RDWR, O_CREAT);
+ add_window(c, bitmap_fd, e.px, e.py, e.sx, e.sy);
+ update_full_display(&main_display, mouse_x, mouse_y);
+ }
+}
+
+typedef struct {
+ int type;
+ struct KEY_EVENT ev;
+} WS_EVENT;
+
+void send_to_client(struct KEY_EVENT ev) {
+ WS_EVENT e = {
+ .type = 0,
+ .ev = ev,
+ };
+ write(active_client->fd, &e, sizeof(e));
+}
+
+void clamp_screen_position(int *x, int *y) {
+ if (0 > *x) {
+ *x = 0;
+ }
+ if (0 > *y) {
+ *y = 0;
+ }
+ if (WIDTH < *x) {
+ *x = WIDTH;
+ }
+ if (HEIGHT < *y) {
+ *y = HEIGHT;
+ }
+}
+
+int windowserver_key_events(struct KEY_EVENT ev, int *redraw) {
+ if (!(ev.mode & (1 << 1)))
+ return 0;
+ if (!active_client)
+ return 0;
+ int x = 0;
+ int y = 0;
+ switch (ev.c) {
+ case 'l':
+ x++;
+ break;
+ case 'h':
+ x--;
+ break;
+ case 'k':
+ y--;
+ break;
+ case 'j':
+ y++;
+ break;
+ }
+ if (x || y) {
+ active_client->w->x += x;
+ active_client->w->y += y;
+ clamp_screen_position(&active_client->w->x, &active_client->w->y);
+ *redraw = 1;
+ return 1;
+ }
+ return 0;
+}
+
+struct mouse_event {
+ uint8_t buttons;
+ uint8_t x;
+ uint8_t y;
+};
+
+void update_mouse(void) {
+ draw_mouse(&main_display, mouse_x, mouse_y);
+ return;
+}
+
+void parse_mouse_event(int fd) {
+ int16_t xc = 0;
+ int16_t yc = 0;
+ int middle_button = 0;
+ for (;;) {
+ struct mouse_event e[100];
+ int rc = read(fd, e, sizeof(e));
+ if (rc <= 0)
+ break;
+
+ int n = rc / sizeof(e[0]);
+ for (int i = 0; i < n; i++) {
+ uint8_t xs = e[i].buttons & (1 << 4);
+ uint8_t ys = e[i].buttons & (1 << 5);
+ middle_button = e[i].buttons & (1 << 2);
+ int16_t x = e[i].x;
+ int16_t y = e[i].y;
+ if (xs)
+ x |= 0xFF00;
+ if (ys)
+ y |= 0xFF00;
+ xc += *(int16_t *)&x;
+ yc += *(int16_t *)&y;
+ }
+ }
+ mouse_x += xc;
+ mouse_y -= yc;
+ if (mouse_x < 0)
+ mouse_x = 0;
+ if (mouse_y < 0)
+ mouse_y = 0;
+ if (middle_button) {
+ active_client->w->x += xc;
+ active_client->w->y -= yc;
+ clamp_screen_position(&active_client->w->x, &active_client->w->y);
+ }
+ update_mouse();
+}
+
+void parse_keyboard_event(int fd) {
+ if (!active_client) {
+ return;
+ }
+ struct KEY_EVENT ev[250];
+ int redraw = 0;
+ for (;;) {
+ int rc;
+ if (0 > (rc = read(fd, &ev[0], sizeof(ev))))
+ break;
+ int n = rc / sizeof(ev[0]);
+ for (int i = 0; i < n; i++) {
+ if (windowserver_key_events(ev[i], &redraw))
+ continue;
+ send_to_client(ev[i]);
+ }
+ }
+ if (redraw)
+ update_full_display(&main_display, mouse_x, mouse_y);
+}
+
+CLIENT *get_client(int fd) {
+ for (int i = 0; i < 100; i++) {
+ if (!clients[i])
+ continue;
+ if (clients[i]->fd == fd)
+ return clients[i];
+ }
+ return NULL;
+}
+
+void kill_client(CLIENT *c) {
+ c->w = NULL;
+ update_full_display(&main_display, mouse_x, mouse_y);
+ active_client = clients[0];
+}
+
+void parse_revents(struct pollfd *fds, size_t s) {
+ for (size_t i = 0; i < s - 1; i++) {
+ if (!fds[i].revents)
+ continue;
+ if (-1 == fds[i].fd)
+ continue;
+ if (socket_fd_poll == i && fds[i].revents & POLLIN) {
+ add_client(fds[i].fd);
+ continue;
+ } else if (keyboard_fd_poll == i) {
+ parse_keyboard_event(fds[i].fd);
+ continue;
+ } else if (mouse_fd_poll == i) {
+ parse_mouse_event(fds[i].fd);
+ continue;
+ }
+ CLIENT *c = get_client(fds[i].fd);
+ assert(c);
+ if (fds[i].revents & POLLHUP) {
+ kill_client(c);
+ fds[i].fd = -1;
+ } else {
+ parse_client_event(c);
+ }
+ }
+}
+
+void run(void) {
+ for (;;) {
+ poll(fds, num_fds, 0);
+ parse_revents(fds, num_fds);
+ reset_revents(fds, num_fds);
+ }
+}
+
+int main(void) {
+ open("/dev/serial", O_WRITE, 0);
+ open("/dev/serial", O_WRITE, 0);
+ setup();
+ run();
+ return 0;
+}
diff --git a/userland/windowserver/ws.h b/userland/windowserver/ws.h
new file mode 100644
index 0000000..c4daf7b
--- /dev/null
+++ b/userland/windowserver/ws.h
@@ -0,0 +1,30 @@
+#ifndef WS_H
+#define WS_H
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+ int bitmap_fd;
+ uint32_t *bitmap_ptr;
+ int x;
+ int y;
+ int sx;
+ int sy;
+} WINDOW;
+
+typedef struct {
+ int fd;
+ WINDOW *w;
+} CLIENT;
+
+typedef struct {
+ int vga_fd;
+ uint8_t *back_buffer;
+ uint8_t *true_buffer;
+ size_t size;
+ uint8_t border_size;
+ uint8_t border_color;
+ uint8_t wallpaper_color;
+ CLIENT **clients;
+} DISPLAY;
+#endif