diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
new file mode 100644
index 0000000..a7f6be4
--- /dev/null
+++ b/.github/workflows/nightly.yml
@@ -0,0 +1,62 @@
+name: Nightly Build
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ build-and-release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Install build dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends \
+ make \
+ gcc-x86-64-linux-gnu \
+ binutils-x86-64-linux-gnu \
+ nasm \
+ xorriso
+ sudo ln -sf /usr/bin/x86_64-linux-gnu-gcc /usr/local/bin/x86_64-elf-gcc
+ sudo ln -sf /usr/bin/x86_64-linux-gnu-ld /usr/local/bin/x86_64-elf-ld
+
+ - name: Build ISO
+ run: make -j4
+
+ - name: Update nightly tag
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git tag -fa nightly -m "Nightly build ${GITHUB_SHA}" "${GITHUB_SHA}"
+ git push origin refs/tags/nightly --force
+
+ - name: Prepare release metadata
+ id: metadata
+ run: |
+ echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
+
+ - name: Publish nightly release asset
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: nightly
+ name: Nightly Build (${{ steps.metadata.outputs.short_sha }})
+ body: |
+ This is an automated nightly build of BoredOS, this is not a final release and may be unstable.
+
+ Built from commit:
+ - Full hash: `${{ github.sha }}`
+ - Short hash: `${{ steps.metadata.outputs.short_sha }}`
+ prerelease: true
+ make_latest: false
+ files: |
+ boredos.iso
+ overwrite_files: true
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index c2dec17..64adf71 100644
--- a/Makefile
+++ b/Makefile
@@ -209,7 +209,7 @@ clean:
run-windows: $(ISO_IMAGE)
qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \
-smp 4 \
- -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
+ -audiodev dsound,id=audio0 -machine pcspk-audiodev=audio0 \
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
-drive file=disk.img,format=raw,file.locking=off
run-mac: $(ISO_IMAGE)
diff --git a/docs/README.md b/docs/README.md
index a44df9d..6f15c6e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -25,7 +25,9 @@ Instructions for compiling the OS from source.
### 3. 🚀 [Application Development](appdev/)
The SDK and toolchain guides for creating your own `.elf` userland binaries.
-- [`SDK Reference`](appdev/sdk_reference.md): Explanation of the custom `libc` wrappers (`stdlib.h`, `string.h`) and system calls.
+- [`SDK Reference`](appdev/sdk_reference.md): Overview hub for SDK layout, includes, and links to detailed libc/syscall docs.
+- [`Syscalls`](appdev/syscalls.md): Current syscall numbers, FS/SYSTEM command IDs, and wrapper guidance.
+- [`libc Reference`](appdev/libc_reference.md): Current libc headers, implemented APIs, and behavior notes.
- [`UI API`](appdev/ui_api.md): Drawing on the screen, creating windows, and polling the event loop using `libui.h`.
- [`Widget API`](appdev/widget_api.md): High-level UI components like buttons, textboxes, and scrollbars using `libwidget.h`.
- [`Custom Apps`](appdev/custom_apps.md): A step-by-step tutorial on writing a new graphical C application, editing the Makefile, and bundling it into the ISO.
diff --git a/docs/appdev/libc_reference.md b/docs/appdev/libc_reference.md
new file mode 100644
index 0000000..1f715e7
--- /dev/null
+++ b/docs/appdev/libc_reference.md
@@ -0,0 +1,184 @@
+# libc Reference
+
+This page documents the current BoredOS userland libc surface from `src/userland/libc/`.
+
+BoredOS libc is a compact implementation focused on the APIs used by in-tree apps. It is not a full glibc replacement.
+
+## Header Overview
+
+| Header | Focus |
+|---|---|
+| `stdlib.h` | allocation, conversion, process helpers |
+| `string.h` | memory/string primitives |
+| `stdio.h` | `FILE*` and formatted I/O |
+| `unistd.h` | POSIX-like fd/process calls |
+| `fcntl.h` | open/fcntl flags, `dup`, `pipe` |
+| `input.h` | keyboard keycode constants |
+| `signal.h` | signal handlers and masks |
+| `sys/stat.h` | `stat`/`fstat` and file mode bits |
+| `sys/types.h` | core typedefs (`pid_t`, `ssize_t`, ...) |
+| `sys/wait.h` | `waitpid` and wait macros |
+| `errno.h` | errno values |
+| `time.h` | time/date utilities |
+| `math.h` | floating-point math helpers |
+| `libui.h` | GUI/window drawing API |
+
+## stdlib.h
+
+Implemented core functions:
+- Memory: `malloc`, `free`, `calloc`, `realloc`
+- Memory aliases: `memset`, `memcpy`
+- Conversions: `atoi`, `itoa`, `strtod`, `abs`
+- Output: `puts`, `printf`
+- Process/environment: `exit`, `_exit`, `sleep`, `chdir`, `getcwd`, `access`, `system`, `getenv`, `abort`
+
+Notes:
+- `sleep` is millisecond-based and maps to kernel sleep command.
+- `system` is a stub-style helper in this libc, not a full shell launcher equivalent.
+
+## string.h
+
+Implemented C string/memory set includes:
+- Memory: `memmove`, `memcmp`, `memcpy`, `memset`, `memchr`
+- Search: `strchr`, `strrchr`, `strpbrk`, `strstr`
+- Span: `strspn`, `strcspn`
+- Compare: `strcmp`, `strncmp`, `strcasecmp`, `strncasecmp`, `strcoll`
+- Build/copy: `strlen`, `strcpy`, `strcat`, `strdup`
+- Errors: `strerror`
+
+## stdio.h
+
+Provided API includes:
+- Stream open/close: `fopen`, `freopen`, `fclose`
+- Read/write: `fread`, `fwrite`, `fgets`, `fputs`, `getc`, `fputc`, `putchar`
+- Positioning: `fseek`, `ftell`, `filelength`
+- Formatting: `fprintf`, `vfprintf`, `snprintf`, `vsnprintf`, `sprintf`, `sscanf`
+- Stream state: `feof`, `ferror`, `clearerr`, `fflush`, `ungetc`
+- Temp/filesystem helpers: `remove`, `rename`, `tmpfile`, `tmpnam`
+
+## unistd.h
+
+Provided POSIX-like interfaces:
+- FD I/O: `read`, `write`, `close`, `lseek`, `isatty`
+- Filesystem: `unlink`
+- Exec family: `execv`, `execve`, `execvp`, `execl`, `execlp`, `execle`
+- Process wait: `waitpid`
+
+Also defines:
+- `SEEK_SET`, `SEEK_CUR`, `SEEK_END`
+- `F_OK`, `X_OK`, `W_OK`, `R_OK`
+
+## fcntl.h
+
+Flags and fd control:
+- Open flags: `O_RDONLY`, `O_WRONLY`, `O_RDWR`, `O_CREAT`, `O_EXCL`, `O_TRUNC`, `O_APPEND`, `O_NONBLOCK`, `O_ACCMODE`
+- fcntl ops: `F_GETFL`, `F_SETFL`
+- FD flag: `FD_CLOEXEC` (declared)
+
+Functions:
+- `open`
+- `fcntl`
+- `dup`
+- `dup2`
+- `pipe`
+
+## input.h
+
+Defines keyboard/control keycode constants used by apps that process
+
+Current constants include:
+- Arrow keys: `KEY_UP`, `KEY_DOWN`, `KEY_LEFT`, `KEY_RIGHT`
+- Controls: `KEY_ENTER`, `KEY_BACKSPACE`, `KEY_ESCAPE`, `KEY_SPACE`, `KEY_ALT`, `KEY_CTRL_L`, `KEY_TAB`
+
+## signal.h
+
+Current signal surface:
+- Basic handler API: `signal`, `raise`, `kill`
+- POSIX-style API: `sigaction`, `sigprocmask`, `sigpending`
+- Types: `sighandler_t`, `sigset_t`, `struct sigaction`
+- Constants: `SIGINT`, `SIGTERM`, `SIGKILL`, `SIG_DFL`, `SIG_IGN`, `SIG_ERR`
+- Mask ops: `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK`
+- Action flags: `SA_RESTART`, `SA_NODEFER`, `SA_RESETHAND`
+
+## ctype.h
+
+Character classification and case conversion:
+- `isdigit`, `isalpha`, `isalnum`, `isspace`
+- `isupper`, `islower`, `isxdigit`
+- `iscntrl`, `ispunct`, `isprint`, `isgraph`
+- `tolower`, `toupper`
+
+## locale.h
+
+Locale stubs and conventions:
+- `struct lconv`
+- `setlocale`
+- `localeconv`
+- `LC_ALL`
+
+## limits.h
+
+Integer and floating-point limit macros:
+- `CHAR_BIT`, `INT_MIN`, `INT_MAX`, `UINT_MAX`
+- `LONG_MIN`, `LONG_MAX`, `ULONG_MAX`
+- `LLONG_MIN`, `LLONG_MAX`, `ULLONG_MAX`
+- `DBL_MAX`
+
+## setjmp.h
+
+Non-local jump support:
+- `jmp_buf`
+- `setjmp`
+- `longjmp`
+
+## time.h
+
+Time/date APIs and types:
+- Types: `time_t`, `clock_t`, `struct tm`
+- Constants: `CLOCKS_PER_SEC`
+- Functions: `time`, `clock`, `localtime`, `gmtime`, `strftime`, `mktime`
+
+## libui.h
+
+Windowing and drawing API used by GUI apps:
+- Window/event: `ui_window_create`, `ui_get_event`, `ui_mark_dirty`, `ui_window_set_title`, `ui_window_set_resizable`
+- Drawing: `ui_draw_rect`, `ui_draw_rounded_rect_filled`, `ui_draw_string`, `ui_draw_string_bitmap`, `ui_draw_image`
+- Text metrics/scaled text: `ui_get_string_width`, `ui_get_font_height`, `ui_draw_string_scaled`, `ui_draw_string_scaled_sloped`, `ui_get_string_width_scaled`, `ui_get_font_height_scaled`
+- System UI helpers: `ui_get_screen_size`, `ui_set_font`
+
+## sys/stat.h and sys/types.h
+
+`sys/stat.h` provides:
+- `struct stat`
+- `stat`, `fstat`, `mkdir`
+- mode/type macros (`S_IFREG`, `S_IFDIR`, `S_ISREG`, `S_ISDIR`, permission bits)
+
+Note:
+- `access` is declared in `stdlib.h` in this libc.
+
+`sys/types.h` provides:
+- `ssize_t`, `off_t`, `mode_t`, `pid_t`, `uid_t`, `gid_t`
+
+## sys/wait.h
+
+- `waitpid`
+- `WNOHANG`
+- status macros: `WEXITSTATUS`, `WIFEXITED`, `WTERMSIG`, `WIFSIGNALED`
+
+## errno.h
+
+Defined errno values include:
+- Generic/input: `EINVAL`, `EDOM`, `ERANGE`, `E2BIG`
+- File/path: `ENOENT`, `EEXIST`, `EISDIR`, `ENOTDIR`, `EBADF`
+- Runtime/state: `ENOMEM`, `EACCES`, `EIO`, `EAGAIN`, `EINTR`, `ECHILD`, `EBUSY`, `EPIPE`, `ESPIPE`, `ENOSYS`, `ENOTSUP`
+
+## Relationship to raw syscalls
+
+- libc high-level I/O and process APIs are backed by wrappers in `src/userland/libc/syscall.c`.
+- Full syscall command IDs and multiplexer details are documented in `docs/appdev/syscalls.md`.
+
+## Practical Guidance
+
+- Prefer libc APIs (`open`, `read`, `write`, `waitpid`, `sigaction`) for portability inside BoredOS userland.
+- Use raw wrapper calls from `syscall.h` only for capabilities that do not yet have higher-level libc wrappers.
+- Avoid numeric `sys_system(...)` command literals in app code; use `SYSTEM_CMD_*` macros.
diff --git a/docs/appdev/sdk_reference.md b/docs/appdev/sdk_reference.md
index 2fed154..92fd65a 100644
--- a/docs/appdev/sdk_reference.md
+++ b/docs/appdev/sdk_reference.md
@@ -1,214 +1,52 @@
Userland SDK Reference
-
Comprehensive manual for custom libc and system calls in BoredOS.
+
Overview and entry point for BoredOS userland development.
---
-BoredOS provides a custom `libc` implementation necessary for writing userland applications (`.elf` binaries). By avoiding a full-blown standard library like `glibc`, the OS ensures a minimal executable footprint tailored strictly to the existing kernel features.
+BoredOS provides a compact userland SDK for building `.elf` applications.
+This page is the high-level map; detailed API references now live in dedicated pages.
-All headers are located in `src/userland/libc/` (standard functions) and `src/wm/` (UI and widgets).
-- `stdlib.h`: Memory, strings, and basic I/O.
-- `math.h`: Freestanding floating-point math library.
-- `libui.h`: Core window and drawing API.
-- `libwidget.h`: High-level UI components.
+## SDK Structure
-## Standard Library (`stdlib.h` & `string.h`)
+Primary headers are in `src/userland/libc/` and UI helpers are in `src/wm/`.
-The standard library wrappers provide memory management, string manipulation, and basic IO formatting without needing direct syscalls.
+- `stdlib.h`, `string.h`, `stdio.h`, `unistd.h`: core libc surface
+- `syscall.h`: raw syscall wrappers and command constants
+- `libui.h`: window creation, drawing, and event polling
+- `libwidget.h`: higher-level reusable widgets
+- `math.h`: freestanding math helpers
-### Memory Allocation
-* `void* malloc(size_t size);` - Allocate a block of memory on the heap.
-* `void free(void* ptr);` - Free a previously allocated memory block.
-* `void* calloc(size_t nmemb, size_t size);` - Allocate and zero-out a block of memory for an array.
-* `void* realloc(void* ptr, size_t size);` - Resize an existing memory block.
+## Detailed References
-### Memory Manipulation (`string.h`)
-* `void* memset(void *s, int c, size_t n);` - Fill a block of memory with a specific byte.
-* `void* memcpy(void *dest, const void *src, size_t n);` - Copy memory from source to destination.
-* `void* memmove(void *dest, const void *src, size_t n);` - Safely copy overlapping memory blocks.
-* `int memcmp(const void *s1, const void *s2, size_t n);` - Compare two memory blocks.
+- [`libc Reference`](libc_reference.md): current libc headers and implemented APIs
+- [`Syscalls`](syscalls.md): syscall numbers, FS/SYSTEM command IDs, and wrappers
+- [`UI API`](ui_api.md): drawing and event APIs
+- [`Widget API`](widget_api.md): common widgets and interaction helpers
-### String Utilities
-* `size_t strlen(const char *s);` - Get the length of a string.
-* `int strcmp(const char *s1, const char *s2);` - Compare two strings lexicographically.
-* `char* strcpy(char *dest, const char *src);` - Copy a string to a destination buffer.
-* `char* strcat(char *dest, const char *src);` - Concatenate two strings.
-
-### Conversion and Formatting
-* `int atoi(const char *nptr);` - String to integer conversion.
-* `void itoa(int n, char *buf);` - Integer to string conversion.
-
-### Input / Output
-* `void puts(const char *s);` - Print a string followed by a newline to the standard output.
-* `void printf(const char *fmt, ...);` - Formatted print to standard output (supports `%d`, `%s`, `%x`, etc.).
-
-### Process Control
-* `void exit(int status);` - Terminate the current process.
-* `void sleep(int ms);` - Pause execution for a specified number of milliseconds.
-
----
-
-## Math Library (`math.h`)
-
-BoredOS ships a freestanding floating-point math library in `libc/math.h`. It uses pure arithmetic — Taylor series, Newton-Raphson, and range-reduction — with no dependency on a host `libm` or hardware math intrinsics. It is automatically linked into every userland ELF.
+## Typical Include Set
```c
-#include "math.h"
+#include
+#include
+#include
+#include
+#include
```
-### Constants
+For GUI apps:
-| Constant | Value | Description |
-|---|---|---|
-| `M_PI` | 3.14159… | π |
-| `M_E` | 2.71828… | Euler's number |
-| `M_LN2` | 0.69315… | Natural log of 2 |
-| `M_SQRT2` | 1.41421… | √2 |
-| `HUGE_VAL` | ~+∞ | Overflow sentinel |
-
-### Functions
-
-#### Absolute value & remainder
-* `double fabs(double x);` — Absolute value.
-* `double fmod(double x, double y);` — Floating-point remainder. Returns `0` when `y == 0`.
-
-#### Rounding
-* `double floor(double x);` — Largest integer ≤ x.
-* `double ceil(double x);` — Smallest integer ≥ x.
-
-#### Trigonometry *(arguments in radians)*
-* `double sin(double x);` — Sine. Range-reduced to `[-π, π]` then computed via 8-term Taylor series.
-* `double cos(double x);` — Cosine. Computed via `sin(x + π/2)`.
-* `double tan(double x);` — Tangent. Returns sentinel `1e15` near poles.
-
-#### Exponential & logarithm
-* `double sqrt(double x);` — Square root via Newton-Raphson (25 iterations). Returns `0` for `x ≤ 0`.
-* `double log(double x);` — Natural logarithm (ln). Returns `-1e30` for `x ≤ 0`.
-* `double log2(double x);` — Base-2 logarithm.
-* `double log10(double x);` — Base-10 logarithm.
-* `double exp(double x);` — e^x. Saturates to `1e300` for `x > 700`, `0` for `x < -700`.
-* `double pow(double base, double exponent);` — General power. Integer exponents use fast binary exponentiation; fractional exponents use `exp(e * log(b))`.
-
-#### Hyperbolic
-* `double sinh(double x);` — Hyperbolic sine.
-* `double cosh(double x);` — Hyperbolic cosine.
-* `double tanh(double x);` — Hyperbolic tangent.
-
-#### Utility
-* `double hypot(double x, double y);` — `sqrt(x² + y²)` without intermediate overflow.
-* `double fmin(double a, double b);` — Minimum of two values.
-* `double fmax(double a, double b);` — Maximum of two values.
-* `double fclamp(double x, double lo, double hi);` — Clamps `x` into `[lo, hi]`.
-
-> [!NOTE]
-> The implementation file is named `libc/libmath.c` (not `libc/math.c`) to avoid a name collision with the `math` CLI calculator app in userland. The public header is still included as `#include "math.h"`.
-
-For advanced operations, `syscall.h` provides direct wrappers into the kernel.
-
-### Process & System Info
-* `void sys_exit(int status);` - Raw exit syscall.
-* `int sys_write(int fd, const char *buf, int len);` - Write to a file descriptor (usually fd 1 for stdout).
-* `void* sys_sbrk(int incr);` - Expand or shrink the process data segment (used internally by `malloc`).
-* `void sys_kill(int pid);` - Send a kill signal to a process.
-* `void sys_yield(void);` - Yield the remainder of the process's timeslice to the scheduler.
-* `int sys_system(int cmd, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);` - Execute a privileged system command (e.g., reboot, shutdown, beep).
-* `int sys_get_os_info(os_info_t *info);` - Populate an `os_info_t` struct with version and environment details.
-* `uint64_t sys_get_shell_config(const char *key);` - Retrieve internal shell configuration values.
-* `void sys_set_text_color(uint32_t color);` - Set the raw CLI text output color.
-
-### File System API (VFS)
-Interacting with files and directories using the Virtual File System.
-* `int sys_open(const char *path, const char *mode);` - Open a file, returning a file descriptor.
-* `int sys_read(int fd, void *buf, uint32_t len);` - Read from an open file.
-* `int sys_write_fs(int fd, const void *buf, uint32_t len);` - Write to an open file.
-* `void sys_close(int fd);` - Close an open file descriptor.
-* `int sys_seek(int fd, int offset, int whence);` - Reposition the file offset.
-* `uint32_t sys_tell(int fd);` - Get the current file offset.
-* `uint32_t sys_size(int fd);` - Get the total file size.
-* `int sys_delete(const char *path);` - Delete a file.
-* `int sys_mkdir(const char *path);` - Create a new directory.
-* `int sys_exists(const char *path);` - Check if a path exists on the filesystem.
-* `int sys_getcwd(char *buf, int size);` - Get the current working directory string.
-* `int sys_chdir(const char *path);` - Change the current working directory.
-* `int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries);` - List directory contents into an array of `FAT32_FileInfo` structs.
-* `int sys_get_file_info(const char *path, FAT32_FileInfo *info);` - Retrieve metadata for a specific file.
-
-### Networking Stack API
-BoredOS includes lwIP for hardware TCP/UDP networking.
-
-#### Configuration and Status
-* `int sys_network_init(void);` - Initialize the network stack.
-* `int sys_network_is_initialized(void);` - Check stack status.
-* `int sys_network_dhcp_acquire(void);` - Request an IP configuration from a DHCP server.
-* `int sys_network_has_ip(void);` - Check if the system has a valid IP address.
-* `int sys_network_get_mac(net_mac_address_t *mac);` - Get the physical MAC address of the NIC.
-* `int sys_network_get_nic_name(char *name_out);` - Get the name of the active network interface card.
-* `int sys_network_get_ip(net_ipv4_address_t *ip);` - Get current local IPv4 address.
-* `int sys_network_set_ip(const net_ipv4_address_t *ip);` - Manually assign a static IP.
-* `int sys_network_get_gateway(net_ipv4_address_t *ip);` - Get the default gateway IP.
-* `int sys_network_get_dns(net_ipv4_address_t *ip);` - Get the primary DNS server IP.
-* `int sys_set_dns_server(const net_ipv4_address_t *ip);` - Set the primary DNS server.
-* `int sys_get_dns_server(net_ipv4_address_t *ip);` - Retrieve configured DNS server.
-* `int sys_network_get_stat(int stat_type);` - Get network statistics (packets in/out, drops, etc.).
-* `void sys_network_force_unlock(void);` - Force release of network stack locks (use with caution).
-
-#### Communication
-* `int sys_icmp_ping(const net_ipv4_address_t *dest_ip);` - Send an ICMP echo request.
-* `int sys_udp_send(const net_ipv4_address_t *dest_ip, uint16_t dest_port, uint16_t src_port, const void *data, size_t data_len);` - Send a raw UDP datagram.
-* `int sys_dns_lookup(const char *name, net_ipv4_address_t *out_ip);` - Resolve a hostname to an IPv4 address.
-* `int sys_tcp_connect(const net_ipv4_address_t *ip, uint16_t port);` - Establish a TCP connection to a remote host.
-* `int sys_tcp_send(const void *data, size_t len);` - Send data over an active TCP socket.
-* `int sys_tcp_recv(void *buf, size_t max_len);` - Receive data from a TCP socket (blocking).
-* `int sys_tcp_recv_nb(void *buf, size_t max_len);` - Receive data from a TCP socket (non-blocking).
-* `int sys_tcp_close(void);` - Close the active TCP socket.
-
----
-
-## Core Data Structures
-
-### `os_info_t`
-Contains detailed build and version information about the OS.
```c
-typedef struct {
- char os_name[64];
- char os_version[64];
- char os_codename[64];
- char kernel_name[64];
- char kernel_version[64];
- char build_date[64];
- char build_time[64];
- char build_arch[64];
-} os_info_t;
+#include
+#include
```
-### `FAT32_FileInfo`
-Represents a filesystem entry.
-```c
-typedef struct {
- char name[256];
- uint32_t size;
- uint8_t is_directory;
- uint32_t start_cluster;
- uint16_t write_date;
- uint16_t write_time;
-} FAT32_FileInfo;
-```
+## Build and Packaging
+
+- Add app source under `src/userland/` (CLI, GUI, or games subfolder).
+- Ensure it is included in the userland build rules/targets.
+- Build from repo root with `make`.
+- Built binaries are copied into initrd under `/bin` by the top-level `Makefile`.
-### `ProcessInfo`
-Provides status information for an active process.
-```c
-typedef struct {
- uint32_t pid;
- char name[64];
- uint64_t ticks;
- size_t used_memory;
-} ProcessInfo;
-```
-### IP / MAC Addresses
-Wrappers for raw byte arrays.
-```c
-typedef struct { uint8_t bytes[6]; } net_mac_address_t;
-typedef struct { uint8_t bytes[4]; } net_ipv4_address_t;
-```
diff --git a/docs/appdev/syscalls.md b/docs/appdev/syscalls.md
new file mode 100644
index 0000000..9b7e924
--- /dev/null
+++ b/docs/appdev/syscalls.md
@@ -0,0 +1,146 @@
+# Syscall Reference
+
+This page documents the current syscall surface in BoredOS as implemented in:
+- `src/sys/syscall.h` (kernel command IDs)
+- `src/userland/libc/syscall.h` (userland wrappers)
+
+Use libc wrappers when possible instead of calling raw syscall numbers directly.
+
+## Top-Level Syscall Numbers
+
+| Number | Name | Purpose |
+|---|---|---|
+| 0 | `SYS_EXIT` (userland header) | Terminate current process |
+| 1 | `SYS_WRITE` | Write to stdout/tty path |
+| 3 | `SYS_GUI` | Window manager and drawing commands |
+| 4 | `SYS_FS` | Filesystem and fd commands |
+| 5 | `SYS_SYSTEM` | System-wide command multiplexer |
+| 9 | `SYS_SBRK` (userland header) | Heap break management |
+| 10 | `SYS_KILL` (userland header) | Kill process by PID |
+| 60 | `SYS_EXIT` (kernel header) | Internal kernel syscall number map |
+
+Notes:
+- Some numbers differ between kernel and userland headers for historical reasons. For app code, rely on wrapper functions in `src/userland/libc/syscall.c`.
+- `SYS_GUI`, `SYS_FS`, and `SYS_SYSTEM` are command multiplexers.
+
+## FS Command IDs (`SYS_FS`)
+
+| ID | Macro | Meaning |
+|---|---|---|
+| 1 | `FS_CMD_OPEN` | Open file |
+| 2 | `FS_CMD_READ` | Read from fd |
+| 3 | `FS_CMD_WRITE` | Write to fd |
+| 4 | `FS_CMD_CLOSE` | Close fd |
+| 5 | `FS_CMD_SEEK` | Seek in file |
+| 6 | `FS_CMD_TELL` | Current offset |
+| 7 | `FS_CMD_LIST` | Directory listing |
+| 8 | `FS_CMD_DELETE` | Delete file |
+| 9 | `FS_CMD_SIZE` | File size |
+| 10 | `FS_CMD_MKDIR` | Create directory |
+| 11 | `FS_CMD_EXISTS` | Path exists check |
+| 12 | `FS_CMD_GETCWD` | Get cwd |
+| 13 | `FS_CMD_CHDIR` | Change cwd |
+| 14 | `FS_CMD_GET_INFO` | File metadata |
+| 15 | `FS_CMD_DUP` | `dup` fd |
+| 16 | `FS_CMD_DUP2` | `dup2` fd |
+| 17 | `FS_CMD_PIPE` | Create pipe |
+| 18 | `FS_CMD_FCNTL` | `fcntl` flags ops |
+
+## SYSTEM Command IDs (`SYS_SYSTEM`)
+
+### Desktop and display
+
+| ID | Macro | Meaning |
+|---|---|---|
+| 1 | `SYSTEM_CMD_SET_BG_COLOR` | Set desktop background color |
+| 2 | `SYSTEM_CMD_SET_BG_PATTERN` | Set desktop background pattern |
+| 3 | `SYSTEM_CMD_SET_WALLPAPER` | Legacy wallpaper command slot |
+| 4 | `SYSTEM_CMD_SET_DESKTOP_PROP` | Set desktop behavior property |
+| 5 | `SYSTEM_CMD_SET_MOUSE_SPEED` | Set mouse speed |
+| 7 | `SYSTEM_CMD_GET_DESKTOP_PROP` | Get desktop property |
+| 8 | `SYSTEM_CMD_GET_MOUSE_SPEED` | Get mouse speed |
+| 9 | `SYSTEM_CMD_GET_WALLPAPER_THUMB` | Legacy wallpaper thumb slot |
+| 10 | `SYSTEM_CMD_CLEAR_SCREEN` | Clear text console |
+| 29 | `SYSTEM_CMD_SET_TEXT_COLOR` | Set console text color |
+| 31 | `SYSTEM_CMD_SET_WALLPAPER_PATH` | Set wallpaper from path |
+| 40 | `SYSTEM_CMD_SET_FONT` | Set active font |
+| 47 | `SYSTEM_CMD_SET_RESOLUTION` | Set display mode |
+
+### Time, power, and system state
+
+| ID | Macro | Meaning |
+|---|---|---|
+| 11 | `SYSTEM_CMD_RTC_GET` | Read RTC datetime |
+| 12 | `SYSTEM_CMD_REBOOT` | Reboot machine |
+| 13 | `SYSTEM_CMD_SHUTDOWN` | Power off machine |
+| 14 | `SYSTEM_CMD_BEEP` | PC speaker beep |
+| 15 | `SYSTEM_CMD_GET_MEM_INFO` | Return total/used memory |
+| 16 | `SYSTEM_CMD_GET_TICKS` | Return scheduler/WM tick count |
+| 28 | `SYSTEM_CMD_GET_SHELL_CONFIG` | Read shell config value |
+| 32 | `SYSTEM_CMD_RTC_SET` | Set RTC datetime |
+| 41 | `SYSTEM_CMD_SET_RAW_MODE` | Terminal raw-mode control |
+| 43 | `SYSTEM_CMD_YIELD` | Yield scheduler timeslice |
+| 46 | `SYSTEM_CMD_SLEEP` | Sleep current process |
+
+### Network
+
+| ID | Macro | Meaning |
+|---|---|---|
+| 6 | `SYSTEM_CMD_NETWORK_INIT` | Init networking |
+| 17 | `SYSTEM_CMD_PCI_LIST` | PCI device list access |
+| 18 | `SYSTEM_CMD_NETWORK_DHCP` | DHCP acquire |
+| 19 | `SYSTEM_CMD_NETWORK_GET_MAC` | Read NIC MAC |
+| 20 | `SYSTEM_CMD_NETWORK_GET_IP` | Read IPv4 |
+| 21 | `SYSTEM_CMD_NETWORK_SET_IP` | Set static IPv4 |
+| 22 | `SYSTEM_CMD_UDP_SEND` | Send UDP packet |
+| 23 | `SYSTEM_CMD_NETWORK_GET_STATS` | Network stats |
+| 24 | `SYSTEM_CMD_NETWORK_GET_GATEWAY` | Read gateway |
+| 25 | `SYSTEM_CMD_NETWORK_GET_DNS` | Read DNS server |
+| 26 | `SYSTEM_CMD_ICMP_PING` | ICMP ping |
+| 27 | `SYSTEM_CMD_NETWORK_IS_INIT` | Network initialized flag |
+| 30 | `SYSTEM_CMD_NETWORK_HAS_IP` | Has IPv4 address flag |
+| 33 | `SYSTEM_CMD_TCP_CONNECT` | TCP connect |
+| 34 | `SYSTEM_CMD_TCP_SEND` | TCP send |
+| 35 | `SYSTEM_CMD_TCP_RECV` | TCP recv (blocking) |
+| 36 | `SYSTEM_CMD_TCP_CLOSE` | TCP close |
+| 37 | `SYSTEM_CMD_DNS_LOOKUP` | DNS lookup |
+| 38 | `SYSTEM_CMD_SET_DNS` | Set DNS server |
+| 39 | `SYSTEM_CMD_NET_UNLOCK` | Force net lock release |
+| 42 | `SYSTEM_CMD_TCP_RECV_NB` | TCP recv (non-blocking) |
+| 48 | `SYSTEM_CMD_NETWORK_GET_NIC_NAME` | NIC name |
+
+### Process, tty, signals
+
+| ID | Macro | Meaning |
+|---|---|---|
+| 50 | `SYSTEM_CMD_PARALLEL_RUN` | Dispatch parallel job |
+| 60 | `SYSTEM_CMD_TTY_CREATE` | Create tty |
+| 61 | `SYSTEM_CMD_TTY_READ_OUT` | Read tty output buffer |
+| 62 | `SYSTEM_CMD_TTY_WRITE_IN` | Write tty input buffer |
+| 63 | `SYSTEM_CMD_TTY_READ_IN` | Read input for current tty |
+| 64 | `SYSTEM_CMD_SPAWN` | Spawn process |
+| 65 | `SYSTEM_CMD_TTY_SET_FG` | Set tty foreground PID |
+| 66 | `SYSTEM_CMD_TTY_GET_FG` | Get tty foreground PID |
+| 67 | `SYSTEM_CMD_TTY_KILL_FG` | Kill tty foreground PID |
+| 68 | `SYSTEM_CMD_TTY_KILL_ALL` | Kill tty process group |
+| 69 | `SYSTEM_CMD_TTY_DESTROY` | Destroy tty |
+| 70 | `SYSTEM_CMD_EXEC` | Exec replace current process |
+| 71 | `SYSTEM_CMD_WAITPID` | Wait/reap child |
+| 72 | `SYSTEM_CMD_KILL_SIGNAL` | Send signal |
+| 73 | `SYSTEM_CMD_SIGACTION` | Set/get handler |
+| 74 | `SYSTEM_CMD_SIGPROCMASK` | Signal mask ops |
+| 75 | `SYSTEM_CMD_SIGPENDING` | Get pending signals |
+
+## Common Wrapper API (`src/userland/libc/syscall.h`)
+
+Typical wrappers used by apps:
+- Process/system: `sys_exit`, `sys_yield`, `sys_spawn`, `sys_exec`, `sys_waitpid`, `sys_kill_signal`
+- Filesystem: `sys_open`, `sys_read`, `sys_write_fs`, `sys_close`, `sys_seek`, `sys_tell`, `sys_size`, `sys_list`
+- Network: `sys_network_init`, `sys_network_dhcp_acquire`, `sys_udp_send`, `sys_tcp_connect`, `sys_tcp_recv_nb`, `sys_dns_lookup`
+- TTY: `sys_tty_create`, `sys_tty_read_out`, `sys_tty_write_in`, `sys_tty_set_fg`
+
+## Best Practices
+
+- Do not hardcode numeric command IDs in app code.
+- Prefer high-level libc calls (`open`, `read`, `waitpid`, `sigaction`) where available.
+- Use `syscall.h` macros when a raw `sys_system` call is still needed.
diff --git a/src/mem/memory_manager.c b/src/mem/memory_manager.c
index 41c9698..8e24831 100644
--- a/src/mem/memory_manager.c
+++ b/src/mem/memory_manager.c
@@ -216,7 +216,7 @@ void* kmalloc_aligned(size_t size, size_t alignment) {
// 4. [Allocated]
// We'll modify block_list[i] and insert others as needed.
- // To keep things simple and maintain sorted order, we update from right to left or carefully.
+ // To keep things simple and maintain sorted order, we update from right to left.
if (padding > 0 && remaining_size > 0) {
// Case 1: Split into 3
diff --git a/src/sys/process.c b/src/sys/process.c
index 3261c32..9dbb2bb 100644
--- a/src/sys/process.c
+++ b/src/sys/process.c
@@ -10,6 +10,7 @@
#include "memory_manager.h"
#include "elf.h"
#include "wm.h"
+#include "vfs.h"
#include "spinlock.h"
#include "smp.h"
#include "lapic.h"
@@ -29,6 +30,47 @@ static uint32_t next_cpu_assign = 1;
static void process_cleanup_inner(process_t *proc);
+static void process_close_fd_inner(process_t *proc, int fd) {
+ if (!proc || fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) {
+ return;
+ }
+
+ if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) {
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (ref) {
+ ref->refs--;
+ if (ref->refs <= 0) {
+ if (ref->file) vfs_close((vfs_file_t *)ref->file);
+ kfree(ref);
+ }
+ }
+ } else if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd];
+ if (pipe) {
+ if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ) pipe->readers--;
+ else pipe->writers--;
+ if (pipe->readers <= 0 && pipe->writers <= 0) {
+ kfree(pipe);
+ }
+ }
+ }
+
+ proc->fds[fd] = NULL;
+ proc->fd_kind[fd] = PROC_FD_KIND_NONE;
+ proc->fd_flags[fd] = 0;
+}
+
+static void process_init_signal_state(process_t *proc) {
+ if (!proc) return;
+ proc->signal_mask = 0;
+ proc->signal_pending = 0;
+ for (int i = 0; i < MAX_SIGNALS; i++) {
+ proc->signal_handlers[i] = 0;
+ proc->signal_action_mask[i] = 0;
+ proc->signal_action_flags[i] = 0;
+ }
+}
+
void process_init(void) {
for (int i = 0; i < MAX_PROCESSES; i++) {
processes[i].pid = 0xFFFFFFFF;
@@ -47,7 +89,16 @@ void process_init(void) {
kernel_proc->fpu_initialized = true;
- for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
+ for (int i = 0; i < MAX_PROCESS_FDS; i++) {
+ kernel_proc->fds[i] = NULL;
+ kernel_proc->fd_kind[i] = 0;
+ kernel_proc->fd_flags[i] = 0;
+ }
+ kernel_proc->parent_pid = 0;
+ kernel_proc->pgid = 0;
+ kernel_proc->exited = false;
+ kernel_proc->exit_status = 0;
+ process_init_signal_state(kernel_proc);
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(kernel_proc->name, "kernel", 7);
@@ -77,12 +128,18 @@ process_t* process_create(void (*entry_point)(void), bool is_user) {
return NULL;
}
+ process_t *parent = process_get_current();
+
new_proc->pid = next_pid++;
new_proc->is_user = is_user;
new_proc->tty_id = -1;
new_proc->kill_pending = false;
+ new_proc->parent_pid = parent ? parent->pid : 0;
+ new_proc->pgid = parent ? parent->pgid : new_proc->pid;
+ new_proc->exited = false;
+ new_proc->exit_status = 0;
+ process_init_signal_state(new_proc);
- process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
@@ -204,7 +261,11 @@ process_t* process_create_elf(const char* filepath, const char* args_str, bool t
new_proc->pml4_phys = paging_create_user_pml4_phys();
if (!new_proc->pml4_phys) return NULL;
- for (int i = 0; i < MAX_PROCESS_FDS; i++) new_proc->fds[i] = NULL;
+ for (int i = 0; i < MAX_PROCESS_FDS; i++) {
+ new_proc->fds[i] = NULL;
+ new_proc->fd_kind[i] = 0;
+ new_proc->fd_flags[i] = 0;
+ }
new_proc->gui_event_head = 0;
new_proc->gui_event_tail = 0;
new_proc->ui_window = NULL;
@@ -213,15 +274,22 @@ process_t* process_create_elf(const char* filepath, const char* args_str, bool t
new_proc->is_terminal_proc = terminal_proc;
new_proc->tty_id = tty_id;
new_proc->kill_pending = false;
+ new_proc->exited = false;
+ new_proc->exit_status = 0;
+ process_init_signal_state(new_proc);
process_t *parent = process_get_current();
if (parent) {
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(new_proc->cwd, parent->cwd, 1024);
+ new_proc->parent_pid = parent->pid;
+ new_proc->pgid = parent->pgid;
} else {
extern void mem_memset(void *dest, int val, size_t len);
mem_memset(new_proc->cwd, 0, 1024);
new_proc->cwd[0] = '/';
+ new_proc->parent_pid = 0;
+ new_proc->pgid = new_proc->pid;
}
// 2. Load ELF executable
@@ -469,7 +537,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
current_process[my_cpu] = next_proc;
- cur->pid = 0xFFFFFFFF;
+ cur->exited = true;
cur->cpu_affinity = 0xFFFFFFFF;
cur->ui_window = NULL;
cur->is_terminal_proc = false;
@@ -570,7 +638,7 @@ uint64_t process_schedule(uint64_t current_rsp) {
process_t* process_get_by_pid(uint32_t pid) {
for (int i = 0; i < MAX_PROCESSES; i++) {
- if (processes[i].pid == pid) return &processes[i];
+ if (processes[i].pid == pid && !processes[i].exited) return &processes[i];
}
return NULL;
}
@@ -593,12 +661,8 @@ static void process_cleanup_inner(process_t *proc) {
proc->ui_window = NULL;
}
- extern void fat32_close(struct FAT32_FileHandle *fh);
for (int i = 0; i < MAX_PROCESS_FDS; i++) {
- if (proc->fds[i]) {
- fat32_close(proc->fds[i]);
- proc->fds[i] = NULL;
- }
+ process_close_fd_inner(proc, i);
}
extern void cmd_process_finished(void);
@@ -613,12 +677,18 @@ static void process_cleanup_inner(process_t *proc) {
}
void process_terminate(process_t *to_delete) {
+ process_terminate_with_status(to_delete, 0);
+}
+
+void process_terminate_with_status(process_t *to_delete, int status) {
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
uint32_t cpu_count = smp_cpu_count();
for (uint32_t c = 0; c < cpu_count && c < MAX_CPUS_SCHED; c++) {
if (current_process[c] == to_delete) {
to_delete->kill_pending = true;
+ to_delete->exit_status = status;
+ to_delete->exited = true;
return;
}
}
@@ -662,13 +732,13 @@ void process_terminate(process_t *to_delete) {
}
// Mark slot as free
- to_delete->pid = 0xFFFFFFFF;
to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->kill_pending = false;
+ to_delete->exited = true;
+ to_delete->exit_status = status;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
- // Defer kernel stack until we switch away from it
- to_delete->kernel_stack_alloc = NULL;
+ if (to_delete->kernel_stack_alloc) kfree(to_delete->kernel_stack_alloc);
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) {
@@ -694,6 +764,8 @@ uint64_t process_terminate_current(void) {
}
process_cleanup_inner(cur);
+ cur->exited = true;
+ cur->exit_status = 0;
// 2. Find previous process in circular list
process_t *prev = cur;
@@ -729,7 +801,6 @@ uint64_t process_terminate_current(void) {
current_process[my_cpu] = next_proc;
// Mark slot as free
- to_delete->pid = 0xFFFFFFFF;
to_delete->cpu_affinity = 0xFFFFFFFF;
to_delete->ui_window = NULL;
to_delete->is_terminal_proc = false;
@@ -756,6 +827,222 @@ uint64_t process_terminate_current(void) {
return next_rsp;
}
+int process_reap(uint32_t caller_pid, uint32_t pid, int *status_out) {
+ process_t *p = NULL;
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ if (processes[i].pid == pid) {
+ p = &processes[i];
+ break;
+ }
+ }
+ if (!p) return -1;
+ if (!p->exited) return -2;
+ if (p->parent_pid != caller_pid && caller_pid != 0) return -1;
+
+ if (status_out) {
+ *status_out = p->exit_status;
+ }
+
+ p->pid = 0xFFFFFFFF;
+ p->parent_pid = 0;
+ p->pgid = 0;
+ p->cpu_affinity = 0xFFFFFFFF;
+ p->exited = false;
+ p->exit_status = 0;
+ p->sleep_until = 0;
+ p->ui_window = NULL;
+ p->is_terminal_proc = false;
+ p->tty_id = -1;
+ p->kill_pending = false;
+ p->used_memory = 0;
+ p->ticks = 0;
+ p->next = NULL;
+ for (int i = 0; i < MAX_PROCESS_FDS; i++) {
+ p->fds[i] = NULL;
+ p->fd_kind[i] = PROC_FD_KIND_NONE;
+ p->fd_flags[i] = 0;
+ }
+ process_init_signal_state(p);
+ return 0;
+}
+
+int process_waitpid(uint32_t caller_pid, int target_pid, int options, int *status_out) {
+ process_t *caller = process_get_by_pid(caller_pid);
+ int found_child = 0;
+ int found_waitable = 0;
+
+ for (int i = 0; i < MAX_PROCESSES; i++) {
+ process_t *p = &processes[i];
+ int match = 0;
+
+ if (p->pid == 0xFFFFFFFF || p->pid == 0 || p->parent_pid != caller_pid) {
+ continue;
+ }
+
+ found_child = 1;
+ if (target_pid > 0) {
+ match = ((int)p->pid == target_pid);
+ } else if (target_pid == -1) {
+ match = 1;
+ } else if (target_pid == 0) {
+ match = (caller && p->pgid == caller->pgid);
+ } else {
+ match = (p->pgid == (uint32_t)(-target_pid));
+ }
+
+ if (!match) {
+ continue;
+ }
+
+ found_waitable = 1;
+ if (!p->exited) {
+ continue;
+ }
+
+ if (process_reap(caller_pid, p->pid, status_out) != 0) {
+ return -1;
+ }
+ return (int)p->pid;
+ }
+
+ if (!found_child || !found_waitable) {
+ return -1;
+ }
+ if (options & 1) {
+ return 0;
+ }
+ return -2;
+}
+
+int process_exec_replace_current(registers_t *regs, const char* filepath, const char* args_str) {
+ process_t *proc = process_get_current();
+ if (!proc || !proc->is_user || !regs || !filepath) return -1;
+
+ uint64_t new_pml4 = paging_create_user_pml4_phys();
+ if (!new_pml4) return -1;
+
+ size_t elf_load_size = 0;
+ uint64_t entry_point = elf_load(filepath, new_pml4, &elf_load_size);
+ if (entry_point == 0) {
+ extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
+ paging_destroy_user_pml4_phys(new_pml4);
+ return -1;
+ }
+
+ size_t user_stack_size = 262144;
+ void* stack = kmalloc_aligned(user_stack_size, 4096);
+ if (!stack) {
+ extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
+ paging_destroy_user_pml4_phys(new_pml4);
+ return -1;
+ }
+
+ for (uint64_t i = 0; i < (user_stack_size / 4096); i++) {
+ paging_map_page(new_pml4, 0x800000 - user_stack_size + (i * 4096), v2p((uint64_t)stack + (i * 4096)), PT_PRESENT | PT_RW | PT_USER);
+ }
+
+ int argc = 1;
+ char *args_buf = (char *)stack + user_stack_size;
+ uint64_t user_args_buf = 0x800000;
+
+ int path_len = 0;
+ while (filepath[path_len]) path_len++;
+ args_buf -= (path_len + 1);
+ user_args_buf -= (path_len + 1);
+ for (int i = 0; i <= path_len; i++) args_buf[i] = filepath[i];
+
+ uint64_t argv_ptrs[32];
+ argv_ptrs[0] = user_args_buf;
+
+ if (args_str) {
+ int i = 0;
+ while (args_str[i] && argc < 31) {
+ while (args_str[i] == ' ') i++;
+ if (!args_str[i]) break;
+
+ int arg_start = i;
+ bool in_quotes = false;
+ if (args_str[i] == '"') {
+ in_quotes = true;
+ i++;
+ arg_start = i;
+ while (args_str[i] && args_str[i] != '"') i++;
+ } else {
+ while (args_str[i] && args_str[i] != ' ') i++;
+ }
+
+ int arg_len = i - arg_start;
+ args_buf -= (arg_len + 1);
+ user_args_buf -= (arg_len + 1);
+ for (int k = 0; k < arg_len; k++) args_buf[k] = args_str[arg_start + k];
+ args_buf[arg_len] = '\0';
+ argv_ptrs[argc++] = user_args_buf;
+ if (in_quotes && args_str[i] == '"') i++;
+ }
+ }
+ argv_ptrs[argc] = 0;
+
+ uint64_t current_user_sp = user_args_buf;
+ current_user_sp &= ~7ULL;
+ args_buf = (char *)((uint64_t)stack + (current_user_sp - (0x800000 - user_stack_size)));
+
+ int argv_size = (argc + 1) * (int)sizeof(uint64_t);
+ args_buf -= argv_size;
+ current_user_sp -= argv_size;
+ uint64_t actual_argv_ptr = current_user_sp;
+ uint64_t *user_argv_array = (uint64_t *)args_buf;
+ for (int i = 0; i <= argc; i++) user_argv_array[i] = argv_ptrs[i];
+
+ current_user_sp &= ~15ULL;
+
+ if (proc->user_stack_alloc) {
+ kfree(proc->user_stack_alloc);
+ }
+ if (proc->pml4_phys) {
+ extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
+ paging_destroy_user_pml4_phys(proc->pml4_phys);
+ }
+
+ proc->pml4_phys = new_pml4;
+ proc->user_stack_alloc = stack;
+ proc->used_memory = elf_load_size + user_stack_size + 65536;
+ proc->heap_start = 0x20000000;
+ proc->heap_end = 0x20000000;
+ proc->sleep_until = 0;
+ process_init_signal_state(proc);
+
+ int last_slash = -1;
+ for (int i = 0; filepath[i]; i++) if (filepath[i] == '/') last_slash = i;
+ const char *filename = (last_slash == -1) ? filepath : (filepath + last_slash + 1);
+ int ni = 0;
+ while (filename[ni] && ni < 63) {
+ proc->name[ni] = filename[ni];
+ ni++;
+ }
+ proc->name[ni] = 0;
+
+ regs->rip = entry_point;
+ regs->rdi = argc;
+ regs->rsi = actual_argv_ptr;
+ regs->rsp = current_user_sp;
+ regs->rax = 0;
+ regs->rbx = 0;
+ regs->rcx = 0;
+ regs->rdx = 0;
+ regs->r8 = 0;
+ regs->r9 = 0;
+ regs->r10 = 0;
+ regs->r11 = 0;
+ regs->r12 = 0;
+ regs->r13 = 0;
+ regs->r14 = 0;
+ regs->r15 = 0;
+ regs->rbp = 0;
+
+ paging_switch_directory(proc->pml4_phys);
+ return 0;
+}
+
// SMP: IPI handler called on AP cores when BSP broadcasts scheduling IPI
uint64_t sched_ipi_handler(registers_t *regs) {
lapic_eoi(); // Acknowledge the IPI
diff --git a/src/sys/process.h b/src/sys/process.h
index 632afc8..820734d 100644
--- a/src/sys/process.h
+++ b/src/sys/process.h
@@ -11,6 +11,26 @@
#define MAX_GUI_EVENTS 32
#define MAX_PROCESS_FDS 16
+#define MAX_SIGNALS 32
+
+#define PROC_FD_KIND_NONE 0
+#define PROC_FD_KIND_FILE 1
+#define PROC_FD_KIND_PIPE_READ 2
+#define PROC_FD_KIND_PIPE_WRITE 3
+
+typedef struct {
+ void *file;
+ int refs;
+} process_fd_file_ref_t;
+
+typedef struct {
+ uint8_t data[4096];
+ uint32_t read_pos;
+ uint32_t write_pos;
+ uint32_t count;
+ int readers;
+ int writers;
+} process_fd_pipe_t;
struct FAT32_FileHandle;
@@ -38,6 +58,8 @@ typedef struct process {
uint64_t heap_end;
void *fds[MAX_PROCESS_FDS];
+ uint8_t fd_kind[MAX_PROCESS_FDS];
+ int fd_flags[MAX_PROCESS_FDS];
void *kernel_stack_alloc;
void *user_stack_alloc;
@@ -57,6 +79,17 @@ typedef struct process {
uint32_t cpu_affinity;
bool is_idle;
char cwd[1024];
+
+ uint32_t parent_pid;
+ uint32_t pgid;
+ bool exited;
+ int exit_status;
+
+ uint64_t signal_mask;
+ uint64_t signal_pending;
+ uint64_t signal_handlers[MAX_SIGNALS];
+ uint64_t signal_action_mask[MAX_SIGNALS];
+ int signal_action_flags[MAX_SIGNALS];
} __attribute__((aligned(16))) process_t;
typedef struct {
@@ -70,13 +103,17 @@ typedef struct {
void process_init(void);
process_t* process_create(void (*entry_point)(void), bool is_user);
process_t* process_create_elf(const char* filepath, const char* args_str, bool terminal_proc, int tty_id);
+int process_exec_replace_current(registers_t *regs, const char* filepath, const char* args_str);
process_t* process_get_current(void);
void process_set_current_for_cpu(uint32_t cpu_id, process_t* p);
process_t* process_get_current_for_cpu(uint32_t cpu_id);
uint64_t process_schedule(uint64_t current_rsp);
uint64_t process_terminate_current(void);
void process_terminate(process_t *proc);
+void process_terminate_with_status(process_t *proc, int status);
process_t* process_get_by_pid(uint32_t pid);
+int process_waitpid(uint32_t caller_pid, int target_pid, int options, int *status_out);
+int process_reap(uint32_t caller_pid, uint32_t pid, int *status_out);
void process_kill_by_tty(int tty_id);
// SMP: IPI handler for AP scheduling
diff --git a/src/sys/syscall.c b/src/sys/syscall.c
index 37dccff..e31fb8e 100644
--- a/src/sys/syscall.c
+++ b/src/sys/syscall.c
@@ -967,6 +967,35 @@ static const syscall_handler_fn gui_cmd_table[GUI_CMD_TABLE_SIZE] = {
[GUI_CMD_GET_DATETIME] = gui_cmd_get_datetime,
};
+#define O_RDONLY 0x0000
+#define O_WRONLY 0x0001
+#define O_RDWR 0x0002
+#define O_APPEND 0x0400
+#define O_NONBLOCK 0x0800
+#define F_GETFL 3
+#define F_SETFL 4
+
+static int fs_alloc_fd_slot(process_t *proc, int start) {
+ for (int i = start; i < MAX_PROCESS_FDS; i++) {
+ if (!proc->fds[i]) return i;
+ }
+ return -1;
+}
+
+static int fs_mode_to_flags(const char *mode) {
+ if (!mode || !mode[0]) return O_RDONLY;
+ if (mode[0] == 'r') {
+ return (mode[1] == '+') ? O_RDWR : O_RDONLY;
+ }
+ if (mode[0] == 'a') {
+ return (mode[1] == '+') ? (O_RDWR | O_APPEND) : (O_WRONLY | O_APPEND);
+ }
+ if (mode[0] == 'w') {
+ return (mode[1] == '+') ? O_RDWR : O_WRONLY;
+ }
+ return O_RDONLY;
+}
+
static uint64_t fs_cmd_open(const syscall_args_t *args) {
process_t *proc = process_get_current();
const char *path = (const char *)args->arg2;
@@ -977,13 +1006,25 @@ static uint64_t fs_cmd_open(const syscall_args_t *args) {
// but let's be explicit if we can.
vfs_file_t *vf = vfs_open(path, mode);
if (!vf) return -1;
-
+
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)kmalloc(sizeof(process_fd_file_ref_t));
+ if (!ref) {
+ vfs_close(vf);
+ return -1;
+ }
+ ref->file = vf;
+ ref->refs = 1;
+
for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i] == NULL) {
- proc->fds[i] = vf;
+ proc->fds[i] = ref;
+ proc->fd_kind[i] = PROC_FD_KIND_FILE;
+ proc->fd_flags[i] = fs_mode_to_flags(mode);
return (uint64_t)i;
}
}
+
+ kfree(ref);
vfs_close(vf);
return -1;
}
@@ -994,7 +1035,35 @@ static uint64_t fs_cmd_read(const syscall_args_t *args) {
void *buf = (void *)args->arg3;
uint32_t len = (uint32_t)args->arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
- return (uint64_t)vfs_read((vfs_file_t*)proc->fds[fd], buf, (int)len);
+
+ if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) {
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (!ref || !ref->file) return -1;
+ return (uint64_t)vfs_read(ref->file, buf, (int)len);
+ }
+
+ if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd];
+ if (!pipe || !buf) return -1;
+ uint8_t *out = (uint8_t *)buf;
+ uint32_t n = 0;
+ while (n < len) {
+ if (pipe->count == 0) {
+ if (pipe->writers == 0) break;
+ if (proc->fd_flags[fd] & O_NONBLOCK) {
+ if (n == 0) return (uint64_t)-1;
+ break;
+ }
+ break;
+ }
+ out[n++] = pipe->data[pipe->read_pos];
+ pipe->read_pos = (pipe->read_pos + 1) % sizeof(pipe->data);
+ pipe->count--;
+ }
+ return n;
+ }
+
+ return -1;
}
static uint64_t fs_cmd_write(const syscall_args_t *args) {
@@ -1003,15 +1072,65 @@ static uint64_t fs_cmd_write(const syscall_args_t *args) {
const void *buf = (const void *)args->arg3;
uint32_t len = (uint32_t)args->arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
- return (uint64_t)vfs_write((vfs_file_t*)proc->fds[fd], buf, (int)len);
+
+ if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) {
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (!ref || !ref->file) return -1;
+ return (uint64_t)vfs_write(ref->file, buf, (int)len);
+ }
+
+ if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd];
+ if (!pipe || !buf) return -1;
+ if (pipe->readers <= 0) return (uint64_t)-1;
+ const uint8_t *in = (const uint8_t *)buf;
+ uint32_t n = 0;
+ while (n < len) {
+ if (pipe->count == sizeof(pipe->data)) {
+ if (proc->fd_flags[fd] & O_NONBLOCK) {
+ if (n == 0) return (uint64_t)-1;
+ break;
+ }
+ break;
+ }
+ pipe->data[pipe->write_pos] = in[n++];
+ pipe->write_pos = (pipe->write_pos + 1) % sizeof(pipe->data);
+ pipe->count++;
+ }
+ return n;
+ }
+
+ return -1;
}
static uint64_t fs_cmd_close(const syscall_args_t *args) {
process_t *proc = process_get_current();
int fd = (int)args->arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
- vfs_close((vfs_file_t*)proc->fds[fd]);
+
+ if (proc->fd_kind[fd] == PROC_FD_KIND_FILE) {
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (ref) {
+ ref->refs--;
+ if (ref->refs <= 0) {
+ if (ref->file) vfs_close(ref->file);
+ kfree(ref);
+ }
+ }
+ } else if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd];
+ if (pipe) {
+ if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ) pipe->readers--;
+ else pipe->writers--;
+ if (pipe->readers <= 0 && pipe->writers <= 0) {
+ kfree(pipe);
+ }
+ }
+ }
+
proc->fds[fd] = NULL;
+ proc->fd_kind[fd] = PROC_FD_KIND_NONE;
+ proc->fd_flags[fd] = 0;
return 0;
}
@@ -1021,21 +1140,142 @@ static uint64_t fs_cmd_seek(const syscall_args_t *args) {
int offset = (int)args->arg3;
int whence = (int)args->arg4; // 0=SET, 1=CUR, 2=END
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
- return (uint64_t)vfs_seek((vfs_file_t*)proc->fds[fd], offset, whence);
+ if (proc->fd_kind[fd] != PROC_FD_KIND_FILE) return -1;
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (!ref || !ref->file) return -1;
+ return (uint64_t)vfs_seek(ref->file, offset, whence);
}
static uint64_t fs_cmd_tell(const syscall_args_t *args) {
process_t *proc = process_get_current();
int fd = (int)args->arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
- return (uint64_t)vfs_file_position((vfs_file_t*)proc->fds[fd]);
+ if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd];
+ return pipe ? pipe->count : 0;
+ }
+ if (proc->fd_kind[fd] != PROC_FD_KIND_FILE) return -1;
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (!ref || !ref->file) return -1;
+ return (uint64_t)vfs_file_position(ref->file);
}
static uint64_t fs_cmd_size(const syscall_args_t *args) {
process_t *proc = process_get_current();
int fd = (int)args->arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
- return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]);
+ if (proc->fd_kind[fd] == PROC_FD_KIND_PIPE_READ || proc->fd_kind[fd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[fd];
+ return pipe ? pipe->count : 0;
+ }
+ if (proc->fd_kind[fd] != PROC_FD_KIND_FILE) return -1;
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[fd];
+ if (!ref || !ref->file) return -1;
+ return (uint64_t)vfs_file_size(ref->file);
+}
+
+static uint64_t fs_cmd_dup(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int oldfd = (int)args->arg2;
+ if (oldfd < 0 || oldfd >= MAX_PROCESS_FDS || !proc->fds[oldfd]) return -1;
+
+ int newfd = fs_alloc_fd_slot(proc, 0);
+ if (newfd < 0) return -1;
+
+ proc->fds[newfd] = proc->fds[oldfd];
+ proc->fd_kind[newfd] = proc->fd_kind[oldfd];
+ proc->fd_flags[newfd] = proc->fd_flags[oldfd];
+
+ if (proc->fd_kind[oldfd] == PROC_FD_KIND_FILE) {
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[oldfd];
+ if (ref) ref->refs++;
+ } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_READ) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd];
+ if (pipe) pipe->readers++;
+ } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd];
+ if (pipe) pipe->writers++;
+ }
+
+ return (uint64_t)newfd;
+}
+
+static uint64_t fs_cmd_dup2(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int oldfd = (int)args->arg2;
+ int newfd = (int)args->arg3;
+ if (oldfd < 0 || oldfd >= MAX_PROCESS_FDS || !proc->fds[oldfd]) return -1;
+ if (newfd < 0 || newfd >= MAX_PROCESS_FDS) return -1;
+ if (oldfd == newfd) return (uint64_t)newfd;
+
+ if (proc->fds[newfd]) {
+ syscall_args_t close_args = *args;
+ close_args.arg2 = (uint64_t)newfd;
+ if (fs_cmd_close(&close_args) != 0) return -1;
+ }
+
+ proc->fds[newfd] = proc->fds[oldfd];
+ proc->fd_kind[newfd] = proc->fd_kind[oldfd];
+ proc->fd_flags[newfd] = proc->fd_flags[oldfd];
+
+ if (proc->fd_kind[oldfd] == PROC_FD_KIND_FILE) {
+ process_fd_file_ref_t *ref = (process_fd_file_ref_t *)proc->fds[oldfd];
+ if (ref) ref->refs++;
+ } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_READ) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd];
+ if (pipe) pipe->readers++;
+ } else if (proc->fd_kind[oldfd] == PROC_FD_KIND_PIPE_WRITE) {
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)proc->fds[oldfd];
+ if (pipe) pipe->writers++;
+ }
+
+ return (uint64_t)newfd;
+}
+
+static uint64_t fs_cmd_pipe(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int *pipefd = (int *)args->arg2;
+ if (!pipefd) return -1;
+
+ int rfd = fs_alloc_fd_slot(proc, 0);
+ if (rfd < 0) return -1;
+ int wfd = fs_alloc_fd_slot(proc, rfd + 1);
+ if (wfd < 0) return -1;
+
+ process_fd_pipe_t *pipe = (process_fd_pipe_t *)kmalloc(sizeof(process_fd_pipe_t));
+ if (!pipe) return -1;
+ mem_memset(pipe, 0, sizeof(*pipe));
+ pipe->readers = 1;
+ pipe->writers = 1;
+
+ proc->fds[rfd] = pipe;
+ proc->fd_kind[rfd] = PROC_FD_KIND_PIPE_READ;
+ proc->fd_flags[rfd] = O_RDONLY;
+
+ proc->fds[wfd] = pipe;
+ proc->fd_kind[wfd] = PROC_FD_KIND_PIPE_WRITE;
+ proc->fd_flags[wfd] = O_WRONLY;
+
+ pipefd[0] = rfd;
+ pipefd[1] = wfd;
+ return 0;
+}
+
+static uint64_t fs_cmd_fcntl(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int fd = (int)args->arg2;
+ int cmd = (int)args->arg3;
+ int val = (int)args->arg4;
+ if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
+
+ if (cmd == F_GETFL) {
+ return (uint64_t)proc->fd_flags[fd];
+ }
+ if (cmd == F_SETFL) {
+ proc->fd_flags[fd] = (proc->fd_flags[fd] & ~(O_APPEND | O_NONBLOCK)) | (val & (O_APPEND | O_NONBLOCK));
+ return 0;
+ }
+ return -1;
}
static uint64_t fs_cmd_list(const syscall_args_t *args) {
@@ -1137,7 +1377,7 @@ static uint64_t fs_cmd_chdir(const syscall_args_t *args) {
}
return -1;
}
-#define FS_CMD_TABLE_SIZE 15
+#define FS_CMD_TABLE_SIZE 19
static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = {
[FS_CMD_OPEN] = fs_cmd_open, // 1
[FS_CMD_READ] = fs_cmd_read, // 2
@@ -1153,6 +1393,10 @@ static const syscall_handler_fn fs_cmd_table[FS_CMD_TABLE_SIZE] = {
[FS_CMD_GETCWD] = fs_cmd_getcwd, // 12
[FS_CMD_CHDIR] = fs_cmd_chdir, // 13
[FS_CMD_GET_INFO] = fs_cmd_get_info, // 14
+ [FS_CMD_DUP] = fs_cmd_dup, // 15
+ [FS_CMD_DUP2] = fs_cmd_dup2, // 16
+ [FS_CMD_PIPE] = fs_cmd_pipe, // 17
+ [FS_CMD_FCNTL] = fs_cmd_fcntl, // 18
};
static uint64_t sys_cmd_set_bg_color(const syscall_args_t *args) {
@@ -1178,7 +1422,7 @@ static uint64_t sys_cmd_set_bg_pattern(const syscall_args_t *args) {
return 0;
}
-static uint64_t sys_cmd_set_wallpaper_obsolete(const syscall_args_t *args) {
+static uint64_t sys_cmd_set_wallpaper(const syscall_args_t *args) {
(void)args;
return -1;
}
@@ -1230,7 +1474,7 @@ static uint64_t sys_cmd_get_mouse_speed(const syscall_args_t *args) {
return mouse_speed;
}
-static uint64_t sys_cmd_get_wallpaper_thumb_obsolete(const syscall_args_t *args) {
+static uint64_t sys_cmd_get_wallpaper_thumb(const syscall_args_t *args) {
(void)args;
return -1;
}
@@ -1270,6 +1514,21 @@ static uint64_t sys_cmd_beep(const syscall_args_t *args) {
return 0;
}
+static uint64_t sys_cmd_get_mem_info(const syscall_args_t *args) {
+ uint64_t *out = (uint64_t *)args->arg2;
+ if (!out) return -1;
+ MemStats stats = memory_get_stats();
+ out[0] = (uint64_t)stats.total_memory;
+ out[1] = (uint64_t)stats.used_memory;
+ return 0;
+}
+
+static uint64_t sys_cmd_get_ticks(const syscall_args_t *args) {
+ (void)args;
+ extern uint32_t wm_get_ticks(void);
+ return (uint64_t)wm_get_ticks();
+}
+
static uint64_t sys_cmd_pci_list(const syscall_args_t *args) {
typedef struct {
uint16_t vendor;
@@ -1661,6 +1920,140 @@ static uint64_t sys_cmd_spawn_process(const syscall_args_t *args) {
return (uint64_t)child->pid;
}
+typedef struct {
+ uint64_t sa_handler;
+ uint64_t sa_mask;
+ int sa_flags;
+} k_sigaction_t;
+
+#define SA_RESETHAND 0x80000000
+#define SIGKILL_NUM 9
+
+static uint64_t sys_cmd_exec_process(const syscall_args_t *args) {
+ const char *user_path = (const char *)args->arg2;
+ const char *user_args = (const char *)args->arg3;
+ if (!user_path) return -1;
+
+ char path_buf[256];
+ int pi = 0;
+ while (pi < 255 && user_path[pi]) {
+ path_buf[pi] = user_path[pi];
+ pi++;
+ }
+ path_buf[pi] = 0;
+
+ char args_buf[512];
+ const char *args_ptr = NULL;
+ if (user_args) {
+ int ai = 0;
+ while (ai < 511 && user_args[ai]) {
+ args_buf[ai] = user_args[ai];
+ ai++;
+ }
+ args_buf[ai] = 0;
+ args_ptr = args_buf;
+ }
+
+ return process_exec_replace_current(args->regs, path_buf, args_ptr);
+}
+
+static uint64_t sys_cmd_waitpid(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int pid = (int)args->arg2;
+ int *status = (int *)args->arg3;
+ int options = (int)args->arg4;
+ if (!proc) return -1;
+
+ int st = 0;
+ int res = process_waitpid(proc->pid, pid, options, &st);
+ if (res == -2) {
+ if (options & 1) return 0; // WNOHANG
+ return (uint64_t)-2;
+ }
+ if (res < 0) return (uint64_t)-1;
+ if (status) *status = st;
+ return (uint64_t)res;
+}
+
+static uint64_t sys_cmd_kill_signal(const syscall_args_t *args) {
+ int pid = (int)args->arg2;
+ int sig = (int)args->arg3;
+ process_t *target;
+ if (pid == -1) {
+ target = process_get_current();
+ } else {
+ target = process_get_by_pid((uint32_t)pid);
+ }
+ if (!target) return -1;
+ if (sig == 0) return 0;
+ if (sig <= 0 || sig >= MAX_SIGNALS) return -1;
+
+ if (sig == 9) {
+ process_terminate_with_status(target, 128 + sig);
+ return 0;
+ }
+
+ target->signal_pending |= (1ULL << (uint32_t)sig);
+ return 0;
+}
+
+static uint64_t sys_cmd_sigaction(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int sig = (int)args->arg2;
+ const k_sigaction_t *act = (const k_sigaction_t *)args->arg3;
+ k_sigaction_t *oldact = (k_sigaction_t *)args->arg4;
+ if (!proc || sig <= 0 || sig >= MAX_SIGNALS) return -1;
+
+ if (oldact) {
+ oldact->sa_handler = proc->signal_handlers[sig];
+ oldact->sa_mask = proc->signal_action_mask[sig];
+ oldact->sa_flags = proc->signal_action_flags[sig];
+ }
+ if (act) {
+ if (sig == SIGKILL_NUM && act->sa_handler != 0) {
+ return -1;
+ }
+ proc->signal_handlers[sig] = act->sa_handler;
+ proc->signal_action_mask[sig] = act->sa_mask;
+ proc->signal_action_flags[sig] = act->sa_flags;
+ }
+ return 0;
+}
+
+static uint64_t sys_cmd_sigprocmask(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ int how = (int)args->arg2;
+ const uint64_t *set = (const uint64_t *)args->arg3;
+ uint64_t *oldset = (uint64_t *)args->arg4;
+ if (!proc) return -1;
+
+ if (oldset) {
+ *oldset = proc->signal_mask;
+ }
+ if (!set) return 0;
+
+ if (how == 0) {
+ proc->signal_mask |= *set;
+ } else if (how == 1) {
+ proc->signal_mask &= ~(*set);
+ } else if (how == 2) {
+ proc->signal_mask = *set;
+ } else {
+ return -1;
+ }
+ proc->signal_mask &= ~(1ULL << SIGKILL_NUM);
+
+ return 0;
+}
+
+static uint64_t sys_cmd_sigpending(const syscall_args_t *args) {
+ process_t *proc = process_get_current();
+ uint64_t *set = (uint64_t *)args->arg2;
+ if (!proc || !set) return -1;
+ *set = proc->signal_pending;
+ return 0;
+}
+
static uint64_t sys_cmd_tty_set_fg(const syscall_args_t *args) {
int tty_id = (int)args->arg2;
int pid = (int)args->arg3;
@@ -1694,61 +2087,69 @@ static uint64_t sys_cmd_tty_destroy(const syscall_args_t *args) {
return tty_destroy(tty_id);
}
-#define SYS_CMD_TABLE_SIZE 70
+#define SYS_CMD_TABLE_SIZE 76
static const syscall_handler_fn sys_cmd_table[SYS_CMD_TABLE_SIZE] = {
- [1] = sys_cmd_set_bg_color,
- [2] = sys_cmd_set_bg_pattern,
- [3] = sys_cmd_set_wallpaper_obsolete,
- [4] = sys_cmd_set_desktop_prop,
- [5] = sys_cmd_set_mouse_speed,
- [6] = sys_cmd_network_init,
- [7] = sys_cmd_get_desktop_prop,
- [8] = sys_cmd_get_mouse_speed,
- [9] = sys_cmd_get_wallpaper_thumb_obsolete,
- [10] = sys_cmd_clear_screen,
- [11] = sys_cmd_rtc_get,
- [12] = sys_cmd_reboot,
- [13] = sys_cmd_shutdown,
- [14] = sys_cmd_beep,
- [17] = sys_cmd_pci_list,
- [18] = sys_cmd_network_dhcp,
- [19] = sys_cmd_network_get_mac,
- [20] = sys_cmd_network_get_ip,
- [21] = sys_cmd_network_set_ip,
- [22] = sys_cmd_udp_send,
- [23] = sys_cmd_network_get_stats,
- [24] = sys_cmd_network_get_gateway,
- [25] = sys_cmd_network_get_dns,
- [26] = sys_cmd_icmp_ping,
- [27] = sys_cmd_network_is_init,
- [28] = sys_cmd_get_shell_config,
- [29] = sys_cmd_set_text_color,
- [30] = sys_cmd_network_has_ip,
- [31] = sys_cmd_set_wallpaper_path,
- [32] = sys_cmd_rtc_set,
- [33] = sys_cmd_tcp_connect,
- [34] = sys_cmd_tcp_send,
- [35] = sys_cmd_tcp_recv,
- [36] = sys_cmd_tcp_close,
- [37] = sys_cmd_dns_lookup,
- [38] = sys_cmd_set_dns,
- [39] = sys_cmd_net_unlock,
- [40] = sys_cmd_set_font,
- [41] = sys_cmd_set_raw_mode,
- [42] = sys_cmd_tcp_recv_nb,
- [47] = sys_cmd_set_resolution,
- [48] = sys_cmd_network_get_nic_name,
- [50] = sys_cmd_parallel_run,
- [60] = sys_cmd_tty_create,
- [61] = sys_cmd_tty_read_out,
- [62] = sys_cmd_tty_write_in,
- [63] = sys_cmd_tty_read_in,
- [64] = sys_cmd_spawn_process,
- [65] = sys_cmd_tty_set_fg,
- [66] = sys_cmd_tty_get_fg,
- [67] = sys_cmd_tty_kill_fg,
- [68] = sys_cmd_tty_kill_all,
- [69] = sys_cmd_tty_destroy,
+ [SYSTEM_CMD_SET_BG_COLOR] = sys_cmd_set_bg_color,
+ [SYSTEM_CMD_SET_BG_PATTERN] = sys_cmd_set_bg_pattern,
+ [SYSTEM_CMD_SET_WALLPAPER] = sys_cmd_set_wallpaper,
+ [SYSTEM_CMD_SET_DESKTOP_PROP] = sys_cmd_set_desktop_prop,
+ [SYSTEM_CMD_SET_MOUSE_SPEED] = sys_cmd_set_mouse_speed,
+ [SYSTEM_CMD_NETWORK_INIT] = sys_cmd_network_init,
+ [SYSTEM_CMD_GET_DESKTOP_PROP] = sys_cmd_get_desktop_prop,
+ [SYSTEM_CMD_GET_MOUSE_SPEED] = sys_cmd_get_mouse_speed,
+ [SYSTEM_CMD_GET_WALLPAPER_THUMB] = sys_cmd_get_wallpaper_thumb,
+ [SYSTEM_CMD_CLEAR_SCREEN] = sys_cmd_clear_screen,
+ [SYSTEM_CMD_RTC_GET] = sys_cmd_rtc_get,
+ [SYSTEM_CMD_REBOOT] = sys_cmd_reboot,
+ [SYSTEM_CMD_SHUTDOWN] = sys_cmd_shutdown,
+ [SYSTEM_CMD_BEEP] = sys_cmd_beep,
+ [SYSTEM_CMD_GET_MEM_INFO] = sys_cmd_get_mem_info,
+ [SYSTEM_CMD_GET_TICKS] = sys_cmd_get_ticks,
+ [SYSTEM_CMD_PCI_LIST] = sys_cmd_pci_list,
+ [SYSTEM_CMD_NETWORK_DHCP] = sys_cmd_network_dhcp,
+ [SYSTEM_CMD_NETWORK_GET_MAC] = sys_cmd_network_get_mac,
+ [SYSTEM_CMD_NETWORK_GET_IP] = sys_cmd_network_get_ip,
+ [SYSTEM_CMD_NETWORK_SET_IP] = sys_cmd_network_set_ip,
+ [SYSTEM_CMD_UDP_SEND] = sys_cmd_udp_send,
+ [SYSTEM_CMD_NETWORK_GET_STATS] = sys_cmd_network_get_stats,
+ [SYSTEM_CMD_NETWORK_GET_GATEWAY] = sys_cmd_network_get_gateway,
+ [SYSTEM_CMD_NETWORK_GET_DNS] = sys_cmd_network_get_dns,
+ [SYSTEM_CMD_ICMP_PING] = sys_cmd_icmp_ping,
+ [SYSTEM_CMD_NETWORK_IS_INIT] = sys_cmd_network_is_init,
+ [SYSTEM_CMD_GET_SHELL_CONFIG] = sys_cmd_get_shell_config,
+ [SYSTEM_CMD_SET_TEXT_COLOR] = sys_cmd_set_text_color,
+ [SYSTEM_CMD_NETWORK_HAS_IP] = sys_cmd_network_has_ip,
+ [SYSTEM_CMD_SET_WALLPAPER_PATH] = sys_cmd_set_wallpaper_path,
+ [SYSTEM_CMD_RTC_SET] = sys_cmd_rtc_set,
+ [SYSTEM_CMD_TCP_CONNECT] = sys_cmd_tcp_connect,
+ [SYSTEM_CMD_TCP_SEND] = sys_cmd_tcp_send,
+ [SYSTEM_CMD_TCP_RECV] = sys_cmd_tcp_recv,
+ [SYSTEM_CMD_TCP_CLOSE] = sys_cmd_tcp_close,
+ [SYSTEM_CMD_DNS_LOOKUP] = sys_cmd_dns_lookup,
+ [SYSTEM_CMD_SET_DNS] = sys_cmd_set_dns,
+ [SYSTEM_CMD_NET_UNLOCK] = sys_cmd_net_unlock,
+ [SYSTEM_CMD_SET_FONT] = sys_cmd_set_font,
+ [SYSTEM_CMD_SET_RAW_MODE] = sys_cmd_set_raw_mode,
+ [SYSTEM_CMD_TCP_RECV_NB] = sys_cmd_tcp_recv_nb,
+ [SYSTEM_CMD_SET_RESOLUTION] = sys_cmd_set_resolution,
+ [SYSTEM_CMD_NETWORK_GET_NIC_NAME] = sys_cmd_network_get_nic_name,
+ [SYSTEM_CMD_PARALLEL_RUN] = sys_cmd_parallel_run,
+ [SYSTEM_CMD_TTY_CREATE] = sys_cmd_tty_create,
+ [SYSTEM_CMD_TTY_READ_OUT] = sys_cmd_tty_read_out,
+ [SYSTEM_CMD_TTY_WRITE_IN] = sys_cmd_tty_write_in,
+ [SYSTEM_CMD_TTY_READ_IN] = sys_cmd_tty_read_in,
+ [SYSTEM_CMD_SPAWN] = sys_cmd_spawn_process,
+ [SYSTEM_CMD_TTY_SET_FG] = sys_cmd_tty_set_fg,
+ [SYSTEM_CMD_TTY_GET_FG] = sys_cmd_tty_get_fg,
+ [SYSTEM_CMD_TTY_KILL_FG] = sys_cmd_tty_kill_fg,
+ [SYSTEM_CMD_TTY_KILL_ALL] = sys_cmd_tty_kill_all,
+ [SYSTEM_CMD_TTY_DESTROY] = sys_cmd_tty_destroy,
+ [SYSTEM_CMD_EXEC] = sys_cmd_exec_process,
+ [SYSTEM_CMD_WAITPID] = sys_cmd_waitpid,
+ [SYSTEM_CMD_KILL_SIGNAL] = sys_cmd_kill_signal,
+ [SYSTEM_CMD_SIGACTION] = sys_cmd_sigaction,
+ [SYSTEM_CMD_SIGPROCMASK] = sys_cmd_sigprocmask,
+ [SYSTEM_CMD_SIGPENDING] = sys_cmd_sigpending,
};
static uint64_t handle_sys_write(const syscall_args_t *args) {
@@ -1871,6 +2272,49 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
return 0;
}
+static uint64_t syscall_maybe_deliver_signal(registers_t *regs) {
+ process_t *proc = process_get_current();
+ if (!proc || !proc->is_user) return (uint64_t)regs;
+
+ uint64_t pending = proc->signal_pending & ~proc->signal_mask;
+ if (!pending) return (uint64_t)regs;
+
+ int sig = -1;
+ for (int i = 1; i < MAX_SIGNALS; i++) {
+ if (pending & (1ULL << (uint32_t)i)) {
+ sig = i;
+ break;
+ }
+ }
+ if (sig < 0) return (uint64_t)regs;
+
+ proc->signal_pending &= ~(1ULL << (uint32_t)sig);
+ uint64_t handler = proc->signal_handlers[sig];
+ int flags = proc->signal_action_flags[sig];
+
+ if (handler == 1) {
+ return (uint64_t)regs;
+ }
+
+ if (handler == 0 || sig == 9) {
+ process_terminate_with_status(proc, 128 + sig);
+ return process_schedule((uint64_t)regs);
+ }
+
+ if (flags & SA_RESETHAND) {
+ proc->signal_handlers[sig] = 0;
+ proc->signal_action_mask[sig] = 0;
+ proc->signal_action_flags[sig] = 0;
+ }
+
+ uint64_t new_rsp = regs->rsp - sizeof(uint64_t);
+ *((uint64_t *)new_rsp) = regs->rip;
+ regs->rsp = new_rsp;
+ regs->rip = handler;
+ regs->rdi = (uint64_t)sig;
+ return (uint64_t)regs;
+}
+
uint64_t syscall_handler_c(registers_t *regs) {
uint64_t syscall_num = regs->rax;
@@ -1899,13 +2343,13 @@ uint64_t syscall_handler_c(registers_t *regs) {
}
}
- if (syscall_num == 5 && regs->rdi == 43) { // SYSTEM_CMD_YIELD
+ if (syscall_num == SYS_SYSTEM && regs->rdi == SYSTEM_CMD_YIELD) {
extern uint64_t process_schedule(uint64_t current_rsp);
regs->rax = 0;
return process_schedule((uint64_t)regs);
}
- if (syscall_num == 5 && regs->rdi == 46) { // SYSTEM_CMD_SLEEP
+ if (syscall_num == SYS_SYSTEM && regs->rdi == SYSTEM_CMD_SLEEP) {
uint32_t ms = (uint32_t)regs->rsi;
process_t *proc = process_get_current();
extern uint32_t wm_get_ticks(void);
@@ -1918,7 +2362,11 @@ uint64_t syscall_handler_c(registers_t *regs) {
// Normal syscalls
regs->rax = syscall_handler_inner(regs);
-
- // Return current RSP to assembly wrapper
- return (uint64_t)regs;
+
+ if (syscall_num == SYS_SYSTEM && regs->rdi == SYSTEM_CMD_WAITPID && regs->rax == (uint64_t)-2) {
+ regs->rax = 0;
+ return process_schedule((uint64_t)regs);
+ }
+
+ return syscall_maybe_deliver_signal(regs);
}
diff --git a/src/sys/syscall.h b/src/sys/syscall.h
index 03a5c12..d98d030 100644
--- a/src/sys/syscall.h
+++ b/src/sys/syscall.h
@@ -31,6 +31,7 @@ typedef struct {
#define SYS_WRITE 1
#define SYS_GUI 3
#define SYS_FS 4
+#define SYS_SYSTEM 5
#define SYS_EXIT 60
// FS Commands
@@ -48,13 +49,74 @@ typedef struct {
#define FS_CMD_GETCWD 12
#define FS_CMD_CHDIR 13
#define FS_CMD_GET_INFO 14
+#define FS_CMD_DUP 15
+#define FS_CMD_DUP2 16
+#define FS_CMD_PIPE 17
+#define FS_CMD_FCNTL 18
+#define SYSTEM_CMD_SET_BG_COLOR 1
+#define SYSTEM_CMD_SET_BG_PATTERN 2
+#define SYSTEM_CMD_SET_WALLPAPER 3
+#define SYSTEM_CMD_SET_DESKTOP_PROP 4
+#define SYSTEM_CMD_SET_MOUSE_SPEED 5
+#define SYSTEM_CMD_NETWORK_INIT 6
+#define SYSTEM_CMD_GET_DESKTOP_PROP 7
+#define SYSTEM_CMD_GET_MOUSE_SPEED 8
+#define SYSTEM_CMD_GET_WALLPAPER_THUMB 9
+#define SYSTEM_CMD_CLEAR_SCREEN 10
+#define SYSTEM_CMD_RTC_GET 11
+#define SYSTEM_CMD_REBOOT 12
+#define SYSTEM_CMD_SHUTDOWN 13
+#define SYSTEM_CMD_BEEP 14
+#define SYSTEM_CMD_GET_MEM_INFO 15
+#define SYSTEM_CMD_GET_TICKS 16
+#define SYSTEM_CMD_PCI_LIST 17
+#define SYSTEM_CMD_NETWORK_DHCP 18
+#define SYSTEM_CMD_NETWORK_GET_MAC 19
+#define SYSTEM_CMD_NETWORK_GET_IP 20
+#define SYSTEM_CMD_NETWORK_SET_IP 21
+#define SYSTEM_CMD_UDP_SEND 22
+#define SYSTEM_CMD_NETWORK_GET_STATS 23
+#define SYSTEM_CMD_NETWORK_GET_GATEWAY 24
+#define SYSTEM_CMD_NETWORK_GET_DNS 25
+#define SYSTEM_CMD_ICMP_PING 26
+#define SYSTEM_CMD_NETWORK_IS_INIT 27
+#define SYSTEM_CMD_GET_SHELL_CONFIG 28
+#define SYSTEM_CMD_SET_TEXT_COLOR 29
+#define SYSTEM_CMD_NETWORK_HAS_IP 30
+#define SYSTEM_CMD_SET_WALLPAPER_PATH 31
+#define SYSTEM_CMD_RTC_SET 32
+#define SYSTEM_CMD_TCP_CONNECT 33
+#define SYSTEM_CMD_TCP_SEND 34
+#define SYSTEM_CMD_TCP_RECV 35
+#define SYSTEM_CMD_TCP_CLOSE 36
+#define SYSTEM_CMD_DNS_LOOKUP 37
+#define SYSTEM_CMD_SET_DNS 38
+#define SYSTEM_CMD_NET_UNLOCK 39
+#define SYSTEM_CMD_SET_FONT 40
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RESOLUTION 47
+#define SYSTEM_CMD_NETWORK_GET_NIC_NAME 48
#define SYSTEM_CMD_PARALLEL_RUN 50
+#define SYSTEM_CMD_TTY_CREATE 60
+#define SYSTEM_CMD_TTY_READ_OUT 61
+#define SYSTEM_CMD_TTY_WRITE_IN 62
+#define SYSTEM_CMD_TTY_READ_IN 63
+#define SYSTEM_CMD_SPAWN 64
+#define SYSTEM_CMD_TTY_SET_FG 65
+#define SYSTEM_CMD_TTY_GET_FG 66
+#define SYSTEM_CMD_TTY_KILL_FG 67
+#define SYSTEM_CMD_TTY_KILL_ALL 68
+#define SYSTEM_CMD_TTY_DESTROY 69
+#define SYSTEM_CMD_EXEC 70
+#define SYSTEM_CMD_WAITPID 71
+#define SYSTEM_CMD_KILL_SIGNAL 72
+#define SYSTEM_CMD_SIGACTION 73
+#define SYSTEM_CMD_SIGPROCMASK 74
+#define SYSTEM_CMD_SIGPENDING 75
void syscall_init(void);
uint64_t syscall_handler_c(registers_t *regs);
diff --git a/src/userland/Makefile b/src/userland/Makefile
index a3d4207..6e2f2fa 100644
--- a/src/userland/Makefile
+++ b/src/userland/Makefile
@@ -59,7 +59,7 @@ $(BIN_DIR)/screenshot.elf: $(LIBC_OBJS) $(BIN_DIR)/screenshot.o $(BIN_DIR)/stb_i
$(LD) $(LDFLAGS) $^ -o $@
$(BIN_DIR)/%.o: games/doom/%.c | $(BIN_DIR)
- $(CC) $(CFLAGS) -Wno-error -Igames/doom -c $< -o $@
+ $(CC) $(CFLAGS) -Wno-error -DBOREDOS -Igames/doom -c $< -o $@
$(BIN_DIR)/doom.elf: $(LIBC_OBJS) $(DOOM_OBJS) $(BIN_DIR)/stb_image.o
$(LD) $(LDFLAGS) $^ -o $@
diff --git a/src/userland/cli/bsh.c b/src/userland/cli/bsh.c
index c5bfe80..564766e 100644
--- a/src/userland/cli/bsh.c
+++ b/src/userland/cli/bsh.c
@@ -564,7 +564,7 @@ static void history_add(const char *line) {
static void get_time_string(char *out, int max_len) {
int dt[6] = {0};
- sys_system(11, (uint64_t)dt, 0, 0, 0);
+ sys_system(SYSTEM_CMD_RTC_GET, (uint64_t)dt, 0, 0, 0);
char hh[4], mm[4];
itoa(dt[3], hh);
itoa(dt[4], mm);
diff --git a/src/userland/cli/clear.c b/src/userland/cli/clear.c
index 6a1de8b..d0211e8 100644
--- a/src/userland/cli/clear.c
+++ b/src/userland/cli/clear.c
@@ -6,6 +6,6 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
- sys_system(10, 0, 0, 0, 0);
+ sys_system(SYSTEM_CMD_CLEAR_SCREEN, 0, 0, 0, 0);
return 0;
}
diff --git a/src/userland/cli/date.c b/src/userland/cli/date.c
index f19a51d..cc32754 100644
--- a/src/userland/cli/date.c
+++ b/src/userland/cli/date.c
@@ -7,7 +7,7 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
int dt[6];
- if (sys_system(11, (uint64_t)dt, 0, 0, 0) == 0) {
+ if (sys_system(SYSTEM_CMD_RTC_GET, (uint64_t)dt, 0, 0, 0) == 0) {
printf("Current Date: %d-%d-%d %d:%d:%d\n", dt[0], dt[1], dt[2], dt[3], dt[4], dt[5]);
} else {
printf("Error: Could not retrieve date.\n");
diff --git a/src/userland/cli/sort.c b/src/userland/cli/sort.c
index d8cd07e..f1fab48 100644
--- a/src/userland/cli/sort.c
+++ b/src/userland/cli/sort.c
@@ -85,7 +85,7 @@ int main(void) {
if (!fb) return 1;
// Seed PRNG with system time (ticks)
- my_srand((unsigned int)sys_system(16, 0, 0, 0, 0));
+ my_srand((unsigned int)sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0));
// Initialize perfect slope
int max_h = WIN_H - 40; // max height
diff --git a/src/userland/cli/third_party/lua/boredos_stubs.h b/src/userland/cli/third_party/lua/boredos_stubs.h
index d78767a..3b44cea 100644
--- a/src/userland/cli/third_party/lua/boredos_stubs.h
+++ b/src/userland/cli/third_party/lua/boredos_stubs.h
@@ -1,1033 +1,7 @@
#ifndef BOREDOS_LUA_STUBS_H
#define BOREDOS_LUA_STUBS_H
-#include
-#include
-#include
-#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "stdlib.h"
-#include "string.h"
#include "syscall.h"
-#include "math.h"
-
-int errno = 0;
-
-static FILE boredos_stdin_obj = {0, 0, 0, 0, 0};
-static FILE boredos_stdout_obj = {1, 0, 0, 0, 0};
-static FILE boredos_stderr_obj = {2, 0, 0, 0, 0};
-FILE *stdin = &boredos_stdin_obj;
-FILE *stdout = &boredos_stdout_obj;
-FILE *stderr = &boredos_stderr_obj;
-
-static int _b_is_leap(int year) {
- return ((year % 4) == 0 && (year % 100) != 0) || ((year % 400) == 0);
-}
-
-static int _b_days_in_month(int year, int month) {
- static const int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- if (month == 1 && _b_is_leap(year)) {
- return 29;
- }
- return mdays[month];
-}
-
-static long long _b_days_before_year(int year) {
- long long y = (long long)year - 1;
- return y * 365 + y / 4 - y / 100 + y / 400;
-}
-
-static long long _b_days_since_epoch(int year, int month, int day) {
- long long days = _b_days_before_year(year) - _b_days_before_year(1970);
- int m;
- for (m = 0; m < month - 1; m++) {
- days += _b_days_in_month(year, m);
- }
- days += (day - 1);
- return days;
-}
-
-static void _b_civil_from_days(long long z, int *year, int *month, int *day) {
- z += 719468;
- long long era = (z >= 0 ? z : z - 146096) / 146097;
- unsigned doe = (unsigned)(z - era * 146097);
- unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
- int y = (int)yoe + (int)era * 400;
- unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
- unsigned mp = (5 * doy + 2) / 153;
- unsigned d = doy - (153 * mp + 2) / 5 + 1;
- unsigned m = mp + (mp < 10 ? 3 : (unsigned)-9);
- y += (m <= 2);
- *year = y;
- *month = (int)m;
- *day = (int)d;
-}
-
-int setjmp(jmp_buf env) {
- __asm__ volatile(
- "movq %%rbx, 0(%0)\n\t"
- "movq %%rbp, 8(%0)\n\t"
- "movq %%r12, 16(%0)\n\t"
- "movq %%r13, 24(%0)\n\t"
- "movq %%r14, 32(%0)\n\t"
- "movq %%r15, 40(%0)\n\t"
- "leaq 8(%%rsp), %%rax\n\t"
- "movq %%rax, 48(%0)\n\t"
- "movq (%%rsp), %%rax\n\t"
- "movq %%rax, 56(%0)\n\t"
- :
- : "r"(env)
- : "rax", "memory");
- return 0;
-}
-
-void longjmp(jmp_buf env, int val) {
- int r = (val == 0) ? 1 : val;
- __asm__ volatile(
- "movq 0(%0), %%rbx\n\t"
- "movq 8(%0), %%rbp\n\t"
- "movq 16(%0), %%r12\n\t"
- "movq 24(%0), %%r13\n\t"
- "movq 32(%0), %%r14\n\t"
- "movq 40(%0), %%r15\n\t"
- "movq 48(%0), %%rsp\n\t"
- "movl %1, %%eax\n\t"
- "movq 56(%0), %%rdx\n\t"
- "jmp *%%rdx\n\t"
- :
- : "r"(env), "r"(r)
- : "rax", "rdx", "memory");
- __builtin_unreachable();
-}
-
-FILE *fopen(const char *path, const char *mode) {
- int fd = sys_open(path, mode);
- FILE *f;
- if (fd < 0) {
- errno = EINVAL;
- return NULL;
- }
- f = (FILE *)malloc(sizeof(FILE));
- if (!f) {
- sys_close(fd);
- errno = ERANGE;
- return NULL;
- }
- f->fd = fd;
- f->eof = 0;
- f->err = 0;
- f->has_ungetc = 0;
- f->ungetc_char = 0;
- return f;
-}
-
-FILE *freopen(const char *path, const char *mode, FILE *stream) {
- int fd;
- if (!stream) {
- return fopen(path, mode);
- }
- if (stream->fd >= 0) {
- sys_close(stream->fd);
- }
- fd = sys_open(path, mode);
- if (fd < 0) {
- stream->err = 1;
- errno = EINVAL;
- return NULL;
- }
- stream->fd = fd;
- stream->eof = 0;
- stream->err = 0;
- stream->has_ungetc = 0;
- return stream;
-}
-
-int fclose(FILE *stream) {
- if (!stream) {
- return EOF;
- }
- if (stream != stdin && stream != stdout && stream != stderr) {
- if (stream->fd >= 0) {
- sys_close(stream->fd);
- }
- free(stream);
- }
- return 0;
-}
-
-size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
- size_t total;
- int n;
- if (!stream || !ptr || size == 0 || nmemb == 0) {
- return 0;
- }
- total = size * nmemb;
- n = sys_read(stream->fd, ptr, (uint32_t)total);
- if (n <= 0) {
- if (n == 0) {
- stream->eof = 1;
- } else {
- stream->err = 1;
- }
- return 0;
- }
- if ((size_t)n < total) {
- stream->eof = 1;
- }
- return (size_t)n / size;
-}
-
-size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
- size_t total;
- int n;
- if (!stream || !ptr || size == 0 || nmemb == 0) {
- return 0;
- }
- total = size * nmemb;
- if (stream->fd <= 2) {
- n = sys_write(stream->fd, (const char *)ptr, (int)total);
- } else {
- n = sys_write_fs(stream->fd, ptr, (uint32_t)total);
- }
- if (n < 0) {
- stream->err = 1;
- return 0;
- }
- return (size_t)n / size;
-}
-
-int fseek(FILE *stream, long offset, int whence) {
- if (!stream) {
- return -1;
- }
- if (sys_seek(stream->fd, (int)offset, whence) < 0) {
- stream->err = 1;
- return -1;
- }
- stream->eof = 0;
- stream->has_ungetc = 0;
- return 0;
-}
-
-long ftell(FILE *stream) {
- if (!stream) {
- return -1;
- }
- return (long)sys_tell(stream->fd);
-}
-
-int getc(FILE *stream) {
- unsigned char ch;
- int n;
- if (!stream) {
- return EOF;
- }
- if (stream->has_ungetc) {
- stream->has_ungetc = 0;
- return stream->ungetc_char;
- }
- n = sys_read(stream->fd, &ch, 1);
- if (n <= 0) {
- if (n == 0) {
- stream->eof = 1;
- } else {
- stream->err = 1;
- }
- return EOF;
- }
- return (int)ch;
-}
-
-int ungetc(int c, FILE *stream) {
- if (!stream || c == EOF) {
- return EOF;
- }
- stream->has_ungetc = 1;
- stream->ungetc_char = (unsigned char)c;
- stream->eof = 0;
- return c;
-}
-
-char *fgets(char *s, int n, FILE *stream) {
- int i;
- if (!s || n <= 0 || !stream) {
- return NULL;
- }
- for (i = 0; i < n - 1; i++) {
- int c = getc(stream);
- if (c == EOF) {
- break;
- }
- s[i] = (char)c;
- if (c == '\n') {
- i++;
- break;
- }
- }
- if (i == 0) {
- return NULL;
- }
- s[i] = '\0';
- return s;
-}
-
-int fputs(const char *s, FILE *stream) {
- size_t len;
- size_t written;
- if (!s || !stream) {
- return EOF;
- }
- len = strlen(s);
- written = fwrite(s, 1, len, stream);
- return (written == len) ? (int)len : EOF;
-}
-
-int feof(FILE *stream) {
- return stream ? stream->eof : 1;
-}
-
-int ferror(FILE *stream) {
- return stream ? stream->err : 1;
-}
-
-void clearerr(FILE *stream) {
- if (stream) {
- stream->eof = 0;
- stream->err = 0;
- }
-}
-
-int fflush(FILE *stream) {
- (void)stream;
- return 0;
-}
-
-int remove(const char *path) {
- return sys_delete(path);
-}
-
-int rename(const char *oldpath, const char *newpath) {
- (void)oldpath;
- (void)newpath;
- errno = EINVAL;
- return -1;
-}
-
-FILE *tmpfile(void) {
- errno = EINVAL;
- return NULL;
-}
-
-char *tmpnam(char *s) {
- (void)s;
- errno = EINVAL;
- return NULL;
-}
-
-int isdigit(int c) { return (c >= '0' && c <= '9'); }
-int isalpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); }
-int isalnum(int c) { return isalpha(c) || isdigit(c); }
-int isspace(int c) {
- return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
-}
-int isupper(int c) { return (c >= 'A' && c <= 'Z'); }
-int islower(int c) { return (c >= 'a' && c <= 'z'); }
-int isxdigit(int c) {
- return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-int iscntrl(int c) { return ((c >= 0 && c < 32) || c == 127); }
-int ispunct(int c) {
- return isprint(c) && !isalnum(c) && !isspace(c);
-}
-int isprint(int c) { return (c >= 32 && c < 127); }
-int isgraph(int c) { return (c > 32 && c < 127); }
-int tolower(int c) { return isupper(c) ? (c - 'A' + 'a') : c; }
-int toupper(int c) { return islower(c) ? (c - 'a' + 'A') : c; }
-
-int abs(int x) {
- return (x < 0) ? -x : x;
-}
-
-int strncmp(const char *s1, const char *s2, size_t n) {
- size_t i;
- for (i = 0; i < n; i++) {
- unsigned char c1 = (unsigned char)s1[i];
- unsigned char c2 = (unsigned char)s2[i];
- if (c1 != c2) {
- return (int)c1 - (int)c2;
- }
- if (c1 == '\0') {
- return 0;
- }
- }
- return 0;
-}
-
-char *strncpy(char *dest, const char *src, size_t n) {
- size_t i;
- for (i = 0; i < n && src[i] != '\0'; i++) {
- dest[i] = src[i];
- }
- for (; i < n; i++) {
- dest[i] = '\0';
- }
- return dest;
-}
-
-char *strncat(char *dest, const char *src, size_t n) {
- size_t dlen = strlen(dest);
- size_t i;
- for (i = 0; i < n && src[i] != '\0'; i++) {
- dest[dlen + i] = src[i];
- }
- dest[dlen + i] = '\0';
- return dest;
-}
-
-char *strrchr(const char *s, int c) {
- const char *last = NULL;
- for (;; s++) {
- if (*s == (char)c) {
- last = s;
- }
- if (*s == '\0') {
- break;
- }
- }
- return (char *)last;
-}
-
-char *strpbrk(const char *s, const char *accept) {
- for (; *s; s++) {
- const char *a;
- for (a = accept; *a; a++) {
- if (*s == *a) {
- return (char *)s;
- }
- }
- }
- return NULL;
-}
-
-size_t strspn(const char *s, const char *accept) {
- size_t n = 0;
- while (*s) {
- if (!strchr(accept, *s)) {
- break;
- }
- n++;
- s++;
- }
- return n;
-}
-
-size_t strcspn(const char *s, const char *reject) {
- size_t n = 0;
- while (*s) {
- if (strchr(reject, *s)) {
- break;
- }
- n++;
- s++;
- }
- return n;
-}
-
-void *memchr(const void *s, int c, size_t n) {
- const unsigned char *p = (const unsigned char *)s;
- size_t i;
- for (i = 0; i < n; i++) {
- if (p[i] == (unsigned char)c) {
- return (void *)(p + i);
- }
- }
- return NULL;
-}
-
-int strcoll(const char *s1, const char *s2) {
- return strcmp(s1, s2);
-}
-
-char *strerror(int errnum) {
- switch (errnum) {
- case 0: return "no error";
- case EDOM: return "domain error";
- case ERANGE: return "range error";
- case EINVAL: return "invalid argument";
- default: return "unknown error";
- }
-}
-
-static int _b_hex_digit(unsigned value, int upper) {
- if (value < 10U) {
- return (int)('0' + value);
- }
- return (int)((upper ? 'A' : 'a') + (value - 10U));
-}
-
-static void _b_append_char(char *out, size_t cap, size_t *idx, int c) {
- if (*idx + 1 < cap) {
- out[*idx] = (char)c;
- }
- (*idx)++;
-}
-
-static void _b_append_strn(char *out, size_t cap, size_t *idx, const char *s, size_t n) {
- size_t i;
- for (i = 0; i < n; i++) {
- _b_append_char(out, cap, idx, s[i]);
- }
-}
-
-static void _b_utoa(unsigned long long v, unsigned base, int upper, char *buf, size_t *len) {
- char tmp[64];
- size_t i = 0;
- if (v == 0) {
- tmp[i++] = '0';
- } else {
- while (v && i < sizeof(tmp)) {
- tmp[i++] = (char)_b_hex_digit((unsigned)(v % base), upper);
- v /= base;
- }
- }
- *len = i;
- while (i > 0) {
- *buf++ = tmp[--i];
- }
-}
-
-static void _b_itoa(long long v, char *buf, size_t *len) {
- unsigned long long uv;
- size_t n = 0;
- if (v < 0) {
- *buf++ = '-';
- n++;
- uv = (unsigned long long)(-(v + 1)) + 1ULL;
- } else {
- uv = (unsigned long long)v;
- }
- _b_utoa(uv, 10U, 0, buf, len);
- *len += n;
-}
-
-static void _b_ftoa(double d, int precision, char *buf, size_t *len) {
- long long ip;
- double frac;
- size_t n = 0;
- if (precision < 0) {
- precision = 6;
- }
- if (d < 0.0) {
- buf[n++] = '-';
- d = -d;
- }
- ip = (long long)d;
- frac = d - (double)ip;
- {
- char ibuf[64];
- size_t ilen = 0;
- _b_utoa((unsigned long long)ip, 10U, 0, ibuf, &ilen);
- memcpy(buf + n, ibuf, ilen);
- n += ilen;
- }
- if (precision > 0) {
- int i;
- buf[n++] = '.';
- for (i = 0; i < precision; i++) {
- int digit;
- frac *= 10.0;
- digit = (int)frac;
- if (digit < 0) digit = 0;
- if (digit > 9) digit = 9;
- buf[n++] = (char)('0' + digit);
- frac -= (double)digit;
- }
- }
- *len = n;
-}
-
-int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
- size_t out_i = 0;
- while (*fmt) {
- if (*fmt != '%') {
- _b_append_char(str, size, &out_i, *fmt++);
- continue;
- }
-
- fmt++;
- if (*fmt == '%') {
- _b_append_char(str, size, &out_i, '%');
- fmt++;
- continue;
- }
-
- while (*fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '#' || *fmt == '0') {
- fmt++;
- }
-
- if (*fmt == '*') {
- (void)va_arg(ap, int);
- fmt++;
- } else {
- while (isdigit((unsigned char)*fmt)) {
- fmt++;
- }
- }
-
- {
- int precision = -1;
- if (*fmt == '.') {
- fmt++;
- precision = 0;
- if (*fmt == '*') {
- precision = va_arg(ap, int);
- fmt++;
- } else {
- while (isdigit((unsigned char)*fmt)) {
- precision = precision * 10 + (*fmt - '0');
- fmt++;
- }
- }
- }
-
- int lcount = 0;
- while (*fmt == 'l') {
- lcount++;
- fmt++;
- }
-
- switch (*fmt) {
- case 'd':
- case 'i': {
- long long v;
- char nbuf[64];
- size_t nlen = 0;
- if (lcount >= 2) v = va_arg(ap, long long);
- else if (lcount == 1) v = va_arg(ap, long);
- else v = va_arg(ap, int);
- _b_itoa(v, nbuf, &nlen);
- _b_append_strn(str, size, &out_i, nbuf, nlen);
- break;
- }
- case 'u':
- case 'x':
- case 'X':
- case 'o': {
- unsigned long long v;
- unsigned base = (*fmt == 'o') ? 8U : ((*fmt == 'u') ? 10U : 16U);
- char nbuf[64];
- size_t nlen = 0;
- int upper = (*fmt == 'X');
- if (lcount >= 2) v = va_arg(ap, unsigned long long);
- else if (lcount == 1) v = va_arg(ap, unsigned long);
- else v = va_arg(ap, unsigned int);
- _b_utoa(v, base, upper, nbuf, &nlen);
- _b_append_strn(str, size, &out_i, nbuf, nlen);
- break;
- }
- case 'c': {
- int c = va_arg(ap, int);
- _b_append_char(str, size, &out_i, c);
- break;
- }
- case 's': {
- const char *s = va_arg(ap, const char *);
- size_t slen;
- if (!s) s = "(null)";
- slen = strlen(s);
- if (precision >= 0 && (size_t)precision < slen) {
- slen = (size_t)precision;
- }
- _b_append_strn(str, size, &out_i, s, slen);
- break;
- }
- case 'p': {
- uintptr_t v = (uintptr_t)va_arg(ap, void *);
- char nbuf[32];
- size_t nlen = 0;
- _b_append_strn(str, size, &out_i, "0x", 2);
- _b_utoa((unsigned long long)v, 16U, 0, nbuf, &nlen);
- _b_append_strn(str, size, &out_i, nbuf, nlen);
- break;
- }
- case 'f':
- case 'g':
- case 'e': {
- double v = va_arg(ap, double);
- char nbuf[96];
- size_t nlen = 0;
- _b_ftoa(v, precision, nbuf, &nlen);
- _b_append_strn(str, size, &out_i, nbuf, nlen);
- break;
- }
- default:
- _b_append_char(str, size, &out_i, '%');
- _b_append_char(str, size, &out_i, *fmt);
- break;
- }
- }
- if (*fmt) {
- fmt++;
- }
- }
-
- if (size > 0) {
- size_t term = (out_i < size - 1) ? out_i : (size - 1);
- str[term] = '\0';
- }
- return (int)out_i;
-}
-
-int snprintf(char *str, size_t size, const char *fmt, ...) {
- int n;
- va_list ap;
- va_start(ap, fmt);
- n = vsnprintf(str, size, fmt, ap);
- va_end(ap);
- return n;
-}
-
-int sprintf(char *str, const char *fmt, ...) {
- int n;
- va_list ap;
- va_start(ap, fmt);
- n = vsnprintf(str, (size_t)-1, fmt, ap);
- va_end(ap);
- return n;
-}
-
-int fprintf(FILE *stream, const char *fmt, ...) {
- char buf[1024];
- int len;
- va_list ap;
- va_start(ap, fmt);
- len = vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- if (len <= 0) {
- return len;
- }
- if ((size_t)len > sizeof(buf)) {
- len = (int)sizeof(buf);
- }
- if (fwrite(buf, 1, (size_t)len, stream) == 0) {
- return -1;
- }
- return len;
-}
-
-int sscanf(const char *str, const char *fmt, ...) {
- (void)str;
- (void)fmt;
- return 0;
-}
-
-double ldexp(double x, int expn) {
- double v = x;
- int i;
- if (expn >= 0) {
- for (i = 0; i < expn; i++) {
- v *= 2.0;
- }
- } else {
- for (i = 0; i < -expn; i++) {
- v *= 0.5;
- }
- }
- return v;
-}
-
-double frexp(double x, int *expn) {
- int e = 0;
- double v = x;
- if (x == 0.0) {
- *expn = 0;
- return 0.0;
- }
- while (fabs(v) >= 1.0) {
- v *= 0.5;
- e++;
- }
- while (fabs(v) > 0.0 && fabs(v) < 0.5) {
- v *= 2.0;
- e--;
- }
- *expn = e;
- return v;
-}
-
-static double _b_atan_series(double x) {
- double x2 = x * x;
- double term = x;
- double sum = x;
- for (int n = 3; n <= 23; n += 2) {
- term *= -x2;
- sum += term / (double)n;
- }
- return sum;
-}
-
-static double _b_atan_precise(double x) {
- if (x < 0.0) {
- return -_b_atan_precise(-x);
- }
- if (x > 1.0) {
- return (M_PI / 2.0) - _b_atan_precise(1.0 / x);
- }
- if (x > 0.5) {
- double y = (x - 1.0) / (x + 1.0);
- return (M_PI / 4.0) + _b_atan_series(y);
- }
- return _b_atan_series(x);
-}
-
-double atan2(double y, double x) {
- if (x > 0.0) {
- return _b_atan_precise(y / x);
- }
- if (x < 0.0) {
- if (y >= 0.0) return _b_atan_precise(y / x) + M_PI;
- return _b_atan_precise(y / x) - M_PI;
- }
- if (y > 0.0) return M_PI / 2.0;
- if (y < 0.0) return -M_PI / 2.0;
- return 0.0;
-}
-
-double asin(double x) {
- if (x > 1.0 || x < -1.0) {
- errno = EDOM;
- return 0.0;
- }
- return atan2(x, sqrt(1.0 - x * x));
-}
-
-double acos(double x) {
- if (x > 1.0 || x < -1.0) {
- errno = EDOM;
- return 0.0;
- }
- return M_PI / 2.0 - asin(x);
-}
-
-static time_t _b_seconds_from_ymdhms(int year, int month, int day, int hour, int minute, int second) {
- long long days = _b_days_since_epoch(year, month, day);
- return (time_t)(days * 86400LL + hour * 3600LL + minute * 60LL + second);
-}
-
-static void _b_fill_tm_from_epoch(time_t t, struct tm *out) {
- long long sec = (long long)t;
- long long days;
- int sod;
- int year, month, day;
-
- if (sec < 0) {
- long long d = ((-sec) + 86399LL) / 86400LL;
- sec += d * 86400LL;
- }
-
- days = sec / 86400LL;
- sod = (int)(sec % 86400LL);
- if (sod < 0) {
- sod += 86400;
- days--;
- }
-
- _b_civil_from_days(days, &year, &month, &day);
-
- out->tm_year = year - 1900;
- out->tm_mon = month - 1;
- out->tm_mday = day;
- out->tm_hour = sod / 3600;
- out->tm_min = (sod % 3600) / 60;
- out->tm_sec = sod % 60;
- out->tm_wday = (int)((days + 4) % 7);
- if (out->tm_wday < 0) out->tm_wday += 7;
-
- {
- long long jan1 = _b_days_since_epoch(year, 1, 1);
- out->tm_yday = (int)(days - jan1);
- }
- out->tm_isdst = 0;
-}
-
-time_t time(time_t *out) {
- int dt[6] = {1970, 1, 1, 0, 0, 0};
- time_t t;
- if (sys_system(SYSTEM_CMD_RTC_GET, 0, (uint64_t)dt, 0, 0) != 0) {
- t = 0;
- } else {
- t = _b_seconds_from_ymdhms(dt[0], dt[1], dt[2], dt[3], dt[4], dt[5]);
- }
- if (out) {
- *out = t;
- }
- return t;
-}
-
-clock_t clock(void) {
- static uint64_t start_tsc = 0;
- unsigned int lo;
- unsigned int hi;
- uint64_t now_tsc;
-
- __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
- now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo;
-
- if (start_tsc == 0) {
- start_tsc = now_tsc;
- __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
- now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo;
- }
-
- return (clock_t)(now_tsc - start_tsc);
-}
-
-struct tm *gmtime(const time_t *timer) {
- static struct tm tmv;
- if (!timer) {
- return NULL;
- }
- _b_fill_tm_from_epoch(*timer, &tmv);
- return &tmv;
-}
-
-struct tm *localtime(const time_t *timer) {
- return gmtime(timer);
-}
-
-size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm) {
- (void)fmt;
- if (!s || max == 0 || !tm) {
- return 0;
- }
- {
- int n = snprintf(s, max, "%04d-%02d-%02d %02d:%02d:%02d",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
- if (n < 0 || (size_t)n >= max) {
- if (max > 0) s[0] = '\0';
- return 0;
- }
- return (size_t)n;
- }
-}
-
-time_t mktime(struct tm *tm) {
- if (!tm) {
- return (time_t)-1;
- }
- return _b_seconds_from_ymdhms(
- tm->tm_year + 1900,
- tm->tm_mon + 1,
- tm->tm_mday,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec);
-}
-
-static struct lconv _b_lconv = {
- ".", "", "", "", "", ".", "", "", "", "",
- 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-char *setlocale(int category, const char *locale) {
- (void)category;
- if (locale == NULL || strcmp(locale, "C") == 0 || strcmp(locale, "") == 0) {
- return "C";
- }
- return NULL;
-}
-
-struct lconv *localeconv(void) {
- return &_b_lconv;
-}
-
-sighandler_t signal(int sig, sighandler_t handler) {
- (void)sig;
- return handler;
-}
-
-int system(const char *command) {
- (void)command;
- errno = EINVAL;
- return -1;
-}
-
-char *getenv(const char *name) {
- (void)name;
- return NULL;
-}
-
-void abort(void) {
- sys_exit(1);
- while (1) {}
-}
-
-double strtod(const char *nptr, char **endptr) {
- const char *p = nptr;
- int sign = 1;
- double value = 0.0;
- double frac = 0.0;
- double scale = 1.0;
- int exp_sign = 1;
- int exp_val = 0;
-
- while (isspace((unsigned char)*p)) p++;
-
- if (*p == '-') {
- sign = -1;
- p++;
- } else if (*p == '+') {
- p++;
- }
-
- while (isdigit((unsigned char)*p)) {
- value = value * 10.0 + (double)(*p - '0');
- p++;
- }
-
- if (*p == '.') {
- p++;
- while (isdigit((unsigned char)*p)) {
- frac = frac * 10.0 + (double)(*p - '0');
- scale *= 10.0;
- p++;
- }
- value += frac / scale;
- }
-
- if (*p == 'e' || *p == 'E') {
- const char *ep = p + 1;
- if (*ep == '-') {
- exp_sign = -1;
- ep++;
- } else if (*ep == '+') {
- ep++;
- }
- if (isdigit((unsigned char)*ep)) {
- p = ep;
- while (isdigit((unsigned char)*p)) {
- exp_val = exp_val * 10 + (*p - '0');
- p++;
- }
- }
- }
-
- if (endptr) {
- *endptr = (char *)p;
- }
-
- if (exp_val != 0) {
- value = ldexp(value, exp_sign * exp_val);
- }
- return sign * value;
-}
#endif
diff --git a/src/userland/cli/third_party/lua/sysinclude/time.h b/src/userland/cli/third_party/lua/sysinclude/time.h
index 8fb30c3..69d4123 100644
--- a/src/userland/cli/third_party/lua/sysinclude/time.h
+++ b/src/userland/cli/third_party/lua/sysinclude/time.h
@@ -3,6 +3,9 @@
#include
+#ifndef BOREDOS_LIBC_TIME_H
+#define BOREDOS_LIBC_TIME_H
+
typedef long long time_t;
typedef unsigned long long clock_t;
@@ -28,3 +31,5 @@ size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm);
time_t mktime(struct tm *tm);
#endif
+
+#endif
diff --git a/src/userland/games/doom/boredos_libc.c b/src/userland/games/doom/boredos_libc.c
deleted file mode 100644
index 165bd0f..0000000
--- a/src/userland/games/doom/boredos_libc.c
+++ /dev/null
@@ -1,219 +0,0 @@
-#include "boredos_libc.h"
-#include
-
-int errno = 0;
-
-static FILE _stderr = {2, 0, 0};
-static FILE _stdout = {1, 0, 0};
-static FILE _stdin = {0, 0, 0};
-
-FILE* stderr = &_stderr;
-FILE* stdout = &_stdout;
-FILE* stdin = &_stdin;
-
-FILE *fopen(const char *path, const char *mode) {
- int fd = sys_open(path, mode);
- if (fd < 0) return NULL;
- FILE *f = malloc(sizeof(FILE));
- f->fd = fd;
- f->eof = 0;
- f->error = 0;
- return f;
-}
-
-int fclose(FILE *stream) {
- if (!stream) return EOF;
- if (stream != stderr && stream != stdout && stream != stdin) {
- sys_close(stream->fd);
- free(stream);
- }
- return 0;
-}
-
-size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
- if (!stream) return 0;
- int bytes = sys_read(stream->fd, ptr, size * nmemb);
- if (bytes < 0) {
- stream->error = 1;
- return 0;
- }
- if (bytes == 0) stream->eof = 1;
- return bytes / size;
-}
-
-size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
- if (!stream) return 0;
- if (stream == stdout || stream == stderr) {
- sys_write(stream->fd, ptr, size * nmemb);
- return nmemb;
- }
- int bytes = sys_write_fs(stream->fd, ptr, size * nmemb);
- if (bytes < 0) {
- stream->error = 1;
- return 0;
- }
- return bytes / size;
-}
-
-int fseek(FILE *stream, long offset, int whence) {
- if (!stream) return -1;
- stream->eof = 0;
- return sys_seek(stream->fd, offset, whence);
-}
-
-long ftell(FILE *stream) {
- if (!stream) return -1;
- return sys_tell(stream->fd);
-}
-
-int remove(const char *pathname) {
- return sys_delete(pathname);
-}
-
-int rename(const char *oldpath, const char *newpath) {
- return -1;
-}
-
-int fputc(int c, FILE *stream) {
- unsigned char ch = c;
- if (fwrite(&ch, 1, 1, stream) != 1) return EOF;
- return ch;
-}
-
-int fputs(const char *s, FILE *stream) {
- size_t len = strlen(s);
- if (fwrite(s, 1, len, stream) != len) return EOF;
- return 0;
-}
-
-long filelength(FILE *f) {
- if (!f) return -1;
- return sys_size(f->fd);
-}
-
-int mkdir(const char *pathname, int mode) {
- return sys_mkdir(pathname);
-}
-
-int access(const char *pathname, int mode) {
- if (sys_exists(pathname)) return 0;
- return -1;
-}
-
-int stat(const char *pathname, struct stat *statbuf) {
- if (sys_exists(pathname)) {
- if (statbuf) {
- statbuf->st_size = 0;
- statbuf->st_mode = 0;
- }
- return 0;
- }
- return -1;
-}
-
-int strncasecmp(const char *s1, const char *s2, size_t n) {
- while (n--) {
- char c1 = tolower(*s1++);
- char c2 = tolower(*s2++);
- if (c1 != c2) return c1 - c2;
- if (!c1) break;
- }
- return 0;
-}
-int strcasecmp(const char *s1, const char *s2) {
- while (1) {
- char c1 = tolower(*s1++);
- char c2 = tolower(*s2++);
- if (c1 != c2) return c1 - c2;
- if (!c1) break;
- }
- return 0;
-}
-char *strncpy(char *dest, const char *src, size_t n) {
- char *ret = dest;
- while (n && *src) { *dest++ = *src++; n--; }
- while (n) { *dest++ = 0; n--; }
- return ret;
-}
-int strncmp(const char *s1, const char *s2, size_t n) {
- while (n--) {
- if (*s1 != *s2) return *s1 - *s2;
- if (!*s1) break;
- s1++; s2++;
- }
- return 0;
-}
-char *strrchr(const char *s, int c) {
- const char *last = NULL;
- while (*s) { if (*s == c) last = s; s++; }
- if (c == 0) last = s;
- return (char*)last;
-}
-char *strdup(const char *s) {
- size_t len = strlen(s) + 1;
- char *dup = malloc(len);
- if (dup) memcpy(dup, s, len);
- return dup;
-}
-
-int toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; }
-int tolower(int c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; }
-int isspace(int c) { return c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\v' || c == '\f'; }
-int isdigit(int c) { return c >= '0' && c <= '9'; }
-int isprint(int c) { return c >= 32 && c <= 126; }
-int isalpha(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
-int isalnum(int c) { return isalpha(c) || isdigit(c); }
-int isgraph(int c) { return c > 32 && c <= 126; }
-int ispunct(int c) { return isprint(c) && !isspace(c) && !isalnum(c); }
-int isupper(int c) { return c >= 'A' && c <= 'Z'; }
-
-void _exit(int status) {
- exit(status);
-}
-
-
-
-int fflush(FILE *stream) { return 0; }
-int abs(int x) { return x < 0 ? -x : x; }
-int putchar(int c) { return fputc(c, stdout); }
-int system(const char *command) { return -1; }
-#define STB_SPRINTF_IMPLEMENTATION
-#define STB_SPRINTF_NOFLOAT
-#include "stb_sprintf.h"
-
-int vfprintf(FILE *stream, const char *format, va_list ap) {
- char buf[1024];
- int len = stbsp_vsnprintf(buf, sizeof(buf), format, ap);
- if (len > 0) fwrite(buf, 1, len, stream);
- return len;
-}
-
-int fprintf(FILE *stream, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- int len = vfprintf(stream, format, ap);
- va_end(ap);
- return len;
-}
-
-int sprintf(char *str, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- int len = stbsp_vsprintf(str, format, ap);
- va_end(ap);
- return len;
-}
-
-int snprintf(char *str, size_t size, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- int len = stbsp_vsnprintf(str, size, format, ap);
- va_end(ap);
- return len;
-}
-
-int vsnprintf(char *str, size_t size, const char *format, va_list ap) {
- return stbsp_vsnprintf(str, size, format, ap);
-}
-
-int sscanf(const char *str, const char *format, ...) { return 0; }
diff --git a/src/userland/games/doom/boredos_libc.h b/src/userland/games/doom/boredos_libc.h
index ff63838..46e428d 100644
--- a/src/userland/games/doom/boredos_libc.h
+++ b/src/userland/games/doom/boredos_libc.h
@@ -5,6 +5,9 @@
#include
#include
#include
+#include
+#include
+#include
#define SEEK_SET 0
#define SEEK_CUR 1
@@ -12,12 +15,6 @@
#define EOF (-1)
-typedef struct {
- int fd;
- int eof;
- int error;
-} FILE;
-
extern FILE* stderr;
extern FILE* stdout;
extern FILE* stdin;
@@ -34,6 +31,14 @@ extern FILE* stdin;
#define W_OK 2
#define X_OK 1
+int open(const char *pathname, int flags, ...);
+int close(int fd);
+ssize_t read(int fd, void *buf, size_t count);
+ssize_t write(int fd, const void *buf, size_t count);
+off_t lseek(int fd, off_t offset, int whence);
+int unlink(const char *pathname);
+int isatty(int fd);
+
FILE *fopen(const char *path, const char *mode);
int fclose(FILE *stream);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
@@ -73,11 +78,6 @@ int isupper(int c);
int mkdir(const char *pathname, int mode);
int access(const char *pathname, int mode);
-
-struct stat {
- int st_size;
- int st_mode;
-};
int stat(const char *pathname, struct stat *statbuf);
char *strstr(const char *haystack, const char *needle);
diff --git a/src/userland/games/doom/hu_stuff.c b/src/userland/games/doom/hu_stuff.c
index b63cac7..253e4c7 100644
--- a/src/userland/games/doom/hu_stuff.c
+++ b/src/userland/games/doom/hu_stuff.c
@@ -25,6 +25,7 @@
#include "deh_main.h"
#include "i_swap.h"
+#include "i_system.h"
#include "i_video.h"
#include "hu_stuff.h"
@@ -288,14 +289,47 @@ void HU_Init(void)
int i;
int j;
+ int fallback_lump = -1;
char buffer[9];
// load the heads-up font
j = HU_FONTSTART;
for (i=0;i= 0)
+ {
+ break;
+ }
+ }
+
+ if (fallback_lump < 0)
+ {
+ fallback_lump = W_CheckNumForName("STTNUM0");
+ }
+ }
+
+ if (lump < 0)
+ {
+ lump = fallback_lump;
+ }
+
+ if (lump < 0)
+ {
+ I_Error("HU_Init: no usable HUD font lumps (missing STCFNxxx/STTNUM0)");
+ }
+
+ hu_font[i] = (patch_t *) W_CacheLumpNum(lump, PU_STATIC);
}
}
diff --git a/src/userland/games/doom/i_system.c b/src/userland/games/doom/i_system.c
index 53ab2c9..adfa390 100644
--- a/src/userland/games/doom/i_system.c
+++ b/src/userland/games/doom/i_system.c
@@ -264,7 +264,7 @@ void I_Quit (void)
#endif
}
-#if !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__)
+#if !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) && !defined(BOREDOS)
#define ZENITY_BINARY "/usr/bin/zenity"
// returns non-zero if zenity is available
@@ -347,7 +347,7 @@ static int ZenityErrorBox(char *message)
return result;
}
-#endif /* !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) */
+#endif /* !defined(_WIN32) && !defined(__MACOSX__) && !defined(__DJGPP__) && !defined(BOREDOS) */
//
@@ -456,7 +456,9 @@ void I_Error (char *error, ...)
#else
{
+#if !defined(BOREDOS)
ZenityErrorBox(msgbuf);
+#endif
}
#endif
diff --git a/src/userland/games/doom/sys/stat.h b/src/userland/games/doom/sys/stat.h
index 9300dee..73aacfe 100644
--- a/src/userland/games/doom/sys/stat.h
+++ b/src/userland/games/doom/sys/stat.h
@@ -1 +1,15 @@
-#include "../boredos_libc.h"
+#ifndef DOOM_COMPAT_SYS_STAT_H
+#define DOOM_COMPAT_SYS_STAT_H
+
+#include "types.h"
+
+struct stat {
+ int st_size;
+ int st_mode;
+};
+
+int stat(const char *pathname, struct stat *statbuf);
+int fstat(int fd, struct stat *statbuf);
+int mkdir(const char *pathname, int mode);
+
+#endif
diff --git a/src/userland/games/doom/sys/types.h b/src/userland/games/doom/sys/types.h
index 9300dee..0cdc230 100644
--- a/src/userland/games/doom/sys/types.h
+++ b/src/userland/games/doom/sys/types.h
@@ -1 +1,8 @@
-#include "../boredos_libc.h"
+#ifndef DOOM_COMPAT_SYS_TYPES_H
+#define DOOM_COMPAT_SYS_TYPES_H
+
+typedef long ssize_t;
+typedef long off_t;
+typedef unsigned int mode_t;
+
+#endif
diff --git a/src/userland/gui/browser.c b/src/userland/gui/browser.c
index ab7aa7f..7c29386 100644
--- a/src/userland/gui/browser.c
+++ b/src/userland/gui/browser.c
@@ -313,7 +313,7 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog
int total = 0;
int last_render = 0;
if (progressive) inc_parse_offset = 0;
- long long last_data_tick = sys_system(16, 0, 0, 0, 0);
+ long long last_data_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
while (1) {
int len = sys_tcp_recv_nb(dest_buf + total, max_len - 1 - total);
@@ -321,7 +321,7 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog
if (len == -2) break;
if (len == 0) {
- long long now = sys_system(16, 0, 0, 0, 0);
+ long long now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
if (now > last_data_tick + 1800) break; // 30 sec timeout
gui_event_t ev;
@@ -353,7 +353,7 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog
continue;
}
- last_data_tick = sys_system(16, 0, 0, 0, 0);
+ last_data_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
total += len;
if (total >= max_len - 1) break;
@@ -461,7 +461,7 @@ static void decode_image(unsigned char *data, int len, RenderElement *el) {
el->img_delays = malloc(frame_count * sizeof(int));
el->img_frame_count = frame_count;
el->img_current_frame = 0;
- el->next_frame_tick = sys_system(16, 0, 0, 0, 0) + (delays[0] * 60 / 1000);
+ el->next_frame_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0) + (delays[0] * 60 / 1000);
uint32_t step_x = (img_w_orig << 16) / fit_w;
uint32_t step_y = (img_h_orig << 16) / fit_h;
@@ -2116,7 +2116,7 @@ int main(int argc, char **argv) {
// Animated GIF progress
bool gif_updated = false;
- long long now = sys_system(16, 0, 0, 0, 0);
+ long long now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
for (int i = 0; i < element_count; i++) {
if (elements[i].tag == TAG_IMG && elements[i].img_frames && elements[i].img_frame_count > 1) {
if (now >= elements[i].next_frame_tick) {
diff --git a/src/userland/gui/settings.c b/src/userland/gui/settings.c
index 38b136c..3d9d690 100644
--- a/src/userland/gui/settings.c
+++ b/src/userland/gui/settings.c
@@ -769,14 +769,14 @@ static void control_panel_paint(ui_window_t win) {
}
static void save_desktop_config(void) {
- sys_system(4 /*SET_DESKTOP_PROP*/, 1, desktop_snap_to_grid, 0, 0);
- sys_system(4, 2, desktop_auto_align, 0, 0);
- sys_system(4, 3, desktop_max_rows_per_col, 0, 0);
- sys_system(4, 4, desktop_max_cols, 0, 0);
+ sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 1, desktop_snap_to_grid, 0, 0);
+ sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 2, desktop_auto_align, 0, 0);
+ sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 3, desktop_max_rows_per_col, 0, 0);
+ sys_system(SYSTEM_CMD_SET_DESKTOP_PROP, 4, desktop_max_cols, 0, 0);
}
static void save_mouse_config(void) {
- sys_system(5 /*SET_MOUSE_SPEED*/, mouse_speed, 0, 0, 0);
+ sys_system(SYSTEM_CMD_SET_MOUSE_SPEED, mouse_speed, 0, 0, 0);
}
static int parse_ip(const char* str, net_ipv4_address_t* ip) {
@@ -802,11 +802,11 @@ static int parse_ip(const char* str, net_ipv4_address_t* ip) {
}
static void fetch_kernel_state(void) {
- desktop_snap_to_grid = sys_system(7 /*GET_DESKTOP_PROP*/, 1, 0, 0, 0);
- desktop_auto_align = sys_system(7, 2, 0, 0, 0);
- desktop_max_rows_per_col = sys_system(7, 3, 0, 0, 0);
- desktop_max_cols = sys_system(7, 4, 0, 0, 0);
- mouse_speed = sys_system(8 /*GET_MOUSE_SPEED*/, 0, 0, 0, 0);
+ desktop_snap_to_grid = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 1, 0, 0, 0);
+ desktop_auto_align = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 2, 0, 0, 0);
+ desktop_max_rows_per_col = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 3, 0, 0, 0);
+ desktop_max_cols = sys_system(SYSTEM_CMD_GET_DESKTOP_PROP, 4, 0, 0, 0);
+ mouse_speed = sys_system(SYSTEM_CMD_GET_MOUSE_SPEED, 0, 0, 0, 0);
net_ipv4_address_t kip;
if (sys_network_get_ip(&kip) == 0) {
@@ -889,7 +889,7 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
if (disp_sel_color == 3) { bpp = 8; mode = 1; }
if (disp_sel_color == 4) { bpp = 8; mode = 2; }
- sys_system(47 /*SET_RESOLUTION*/, w, h, bpp, mode);
+ sys_system(SYSTEM_CMD_SET_RESOLUTION, w, h, bpp, mode);
}
return;
}
@@ -912,28 +912,28 @@ static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click
uint32_t c = 0;
if (i==0) c = COLOR_COFFEE; else if(i==1) c = COLOR_TEAL; else if(i==2) c = COLOR_GREEN;
else if(i==3) c = COLOR_BLUE_BG; else if(i==4) c = COLOR_PURPLE; else if(i==5) c = COLOR_GREY;
- sys_system(1, c, 0, 0, 0); btn_wp_colors[i].pressed=false;
+ sys_system(SYSTEM_CMD_SET_BG_COLOR, c, 0, 0, 0); btn_wp_colors[i].pressed=false;
}
return;
}
}
if (widget_button_handle_mouse(&btn_wp_patterns[0], x, y, is_down, is_click, NULL)) {
- if (is_click) { sys_system(2, (uint64_t)pattern_lumberjack, 0, 0, 0); btn_wp_patterns[0].pressed=false;} return;
+ if (is_click) { sys_system(SYSTEM_CMD_SET_BG_PATTERN, (uint64_t)pattern_lumberjack, 0, 0, 0); btn_wp_patterns[0].pressed=false;} return;
}
if (widget_button_handle_mouse(&btn_wp_patterns[1], x, y, is_down, is_click, NULL)) {
- if (is_click) { sys_system(2, (uint64_t)pattern_blue_diamond, 0, 0, 0); btn_wp_patterns[1].pressed=false;} return;
+ if (is_click) { sys_system(SYSTEM_CMD_SET_BG_PATTERN, (uint64_t)pattern_blue_diamond, 0, 0, 0); btn_wp_patterns[1].pressed=false;} return;
}
if (widget_button_handle_mouse(&btn_wp_apply, x, y, is_down, is_click, NULL)) {
if (is_click) {
uint32_t cust = parse_rgb_separate(rgb_r, rgb_g, rgb_b);
- sys_system(1, cust, 0, 0, 0);
+ sys_system(SYSTEM_CMD_SET_BG_COLOR, cust, 0, 0, 0);
btn_wp_apply.pressed=false;
}
return;
}
for (int i=0; i 0);
if (viewer_has_image) {
- viewer_next_frame_tick = sys_system(16, 0, 0, 0, 0) + (viewer_delays[0] * 60 / 1000);
+ viewer_next_frame_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0) + (viewer_delays[0] * 60 / 1000);
}
}
free(delays);
@@ -337,7 +337,7 @@ int main(int argc, char **argv) {
win_w = ev.arg1;
win_h = ev.arg2;
resize_pending = true;
- last_resize_tick = sys_system(16, 0, 0, 0, 0);
+ last_resize_tick = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
// Fast background clear during active resize
ui_draw_rect(win, 0, 0, win_w, win_h, 0xFF000000);
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
@@ -348,7 +348,7 @@ int main(int argc, char **argv) {
}
} else {
if (resize_pending) {
- uint64_t now = sys_system(16, 0, 0, 0, 0);
+ uint64_t now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
if (now > last_resize_tick + 10) {
viewer_paint(win);
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
@@ -357,7 +357,7 @@ int main(int argc, char **argv) {
}
if (viewer_has_image && viewer_frame_count > 1) {
- uint64_t now = sys_system(16, 0, 0, 0, 0);
+ uint64_t now = sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
if (now >= viewer_next_frame_tick) {
viewer_current_frame = (viewer_current_frame + 1) % viewer_frame_count;
viewer_next_frame_tick = now + (viewer_delays[viewer_current_frame] * 60 / 1000);
diff --git a/src/userland/libc/ctype.c b/src/userland/libc/ctype.c
new file mode 100644
index 0000000..7783d36
--- /dev/null
+++ b/src/userland/libc/ctype.c
@@ -0,0 +1,21 @@
+#include "ctype.h"
+
+__attribute__((weak)) int isdigit(int c) { return (c >= '0' && c <= '9'); }
+__attribute__((weak)) int isalpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); }
+__attribute__((weak)) int isalnum(int c) { return isalpha(c) || isdigit(c); }
+__attribute__((weak)) int isspace(int c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+__attribute__((weak)) int isupper(int c) { return (c >= 'A' && c <= 'Z'); }
+__attribute__((weak)) int islower(int c) { return (c >= 'a' && c <= 'z'); }
+__attribute__((weak)) int isxdigit(int c) {
+ return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+__attribute__((weak)) int iscntrl(int c) { return ((c >= 0 && c < 32) || c == 127); }
+__attribute__((weak)) int isprint(int c) { return (c >= 32 && c < 127); }
+__attribute__((weak)) int isgraph(int c) { return (c > 32 && c < 127); }
+__attribute__((weak)) int ispunct(int c) {
+ return isprint(c) && !isalnum(c) && !isspace(c);
+}
+__attribute__((weak)) int tolower(int c) { return isupper(c) ? (c - 'A' + 'a') : c; }
+__attribute__((weak)) int toupper(int c) { return islower(c) ? (c - 'a' + 'A') : c; }
diff --git a/src/userland/libc/ctype.h b/src/userland/libc/ctype.h
new file mode 100644
index 0000000..4960be3
--- /dev/null
+++ b/src/userland/libc/ctype.h
@@ -0,0 +1,18 @@
+#ifndef BOREDOS_LIBC_CTYPE_H
+#define BOREDOS_LIBC_CTYPE_H
+
+int isdigit(int c);
+int isalpha(int c);
+int isalnum(int c);
+int isspace(int c);
+int isupper(int c);
+int islower(int c);
+int isxdigit(int c);
+int iscntrl(int c);
+int ispunct(int c);
+int isprint(int c);
+int isgraph(int c);
+int tolower(int c);
+int toupper(int c);
+
+#endif
diff --git a/src/userland/libc/errno.c b/src/userland/libc/errno.c
new file mode 100644
index 0000000..8a8ab21
--- /dev/null
+++ b/src/userland/libc/errno.c
@@ -0,0 +1,3 @@
+#include "errno.h"
+
+__attribute__((weak)) int errno = 0;
diff --git a/src/userland/libc/errno.h b/src/userland/libc/errno.h
new file mode 100644
index 0000000..396ca00
--- /dev/null
+++ b/src/userland/libc/errno.h
@@ -0,0 +1,27 @@
+#ifndef BOREDOS_LIBC_ERRNO_H
+#define BOREDOS_LIBC_ERRNO_H
+
+extern int errno;
+
+#define EDOM 33
+#define E2BIG 7
+#define ERANGE 34
+#define EINVAL 22
+#define EISDIR 21
+#define ENOENT 2
+#define ENOMEM 12
+#define EACCES 13
+#define EBADF 9
+#define EIO 5
+#define EEXIST 17
+#define EAGAIN 11
+#define EINTR 4
+#define ECHILD 10
+#define ENOTSUP 95
+#define EPIPE 32
+#define ENOTDIR 20
+#define EBUSY 16
+#define ESPIPE 29
+#define ENOSYS 38
+
+#endif
diff --git a/src/userland/libc/fcntl.h b/src/userland/libc/fcntl.h
new file mode 100644
index 0000000..6bb56c1
--- /dev/null
+++ b/src/userland/libc/fcntl.h
@@ -0,0 +1,27 @@
+#ifndef BOREDOS_LIBC_FCNTL_H
+#define BOREDOS_LIBC_FCNTL_H
+
+#include "sys/types.h"
+
+#define O_RDONLY 0x0000
+#define O_WRONLY 0x0001
+#define O_RDWR 0x0002
+#define O_ACCMODE 0x0003
+#define O_CREAT 0x0040
+#define O_EXCL 0x0080
+#define O_TRUNC 0x0200
+#define O_APPEND 0x0400
+#define O_NONBLOCK 0x0800
+
+#define F_GETFL 3
+#define F_SETFL 4
+
+#define FD_CLOEXEC 1
+
+int open(const char *pathname, int flags, ...);
+int fcntl(int fd, int cmd, ...);
+int dup(int oldfd);
+int dup2(int oldfd, int newfd);
+int pipe(int pipefd[2]);
+
+#endif
diff --git a/src/userland/libc/libui.c b/src/userland/libc/libui.c
index 7366992..3a87672 100644
--- a/src/userland/libc/libui.c
+++ b/src/userland/libc/libui.c
@@ -10,7 +10,6 @@ extern uint64_t syscall6(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_
// sys_gui uses syscall #3
#define SYS_GUI 3
-#define GUI_CMD_GET_SCREEN_SIZE 17
ui_window_t ui_window_create(const char *title, int x, int y, int w, int h) {
uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h };
diff --git a/src/userland/libc/libui.h b/src/userland/libc/libui.h
index e96c237..6d98091 100644
--- a/src/userland/libc/libui.h
+++ b/src/userland/libc/libui.h
@@ -22,6 +22,10 @@
#define GUI_CMD_WINDOW_SET_TITLE 15
#define GUI_CMD_SET_FONT 16
#define GUI_CMD_DRAW_STRING_SCALED_SLOPED 18
+#define GUI_CMD_GET_SCREEN_SIZE 50
+#define GUI_CMD_GET_SCREENBUFFER 51
+#define GUI_CMD_SHOW_NOTIFICATION 52
+#define GUI_CMD_GET_DATETIME 53
// Event Types
#define GUI_EVENT_NONE 0
diff --git a/src/userland/libc/limits.h b/src/userland/libc/limits.h
new file mode 100644
index 0000000..760f213
--- /dev/null
+++ b/src/userland/libc/limits.h
@@ -0,0 +1,22 @@
+#ifndef BOREDOS_LIBC_LIMITS_H
+#define BOREDOS_LIBC_LIMITS_H
+
+#define CHAR_BIT 8
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+#define UCHAR_MAX 255
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+#define USHRT_MAX 65535
+#define INT_MIN (-2147483647 - 1)
+#define INT_MAX 2147483647
+#define UINT_MAX 4294967295U
+#define LONG_MIN (-9223372036854775807L - 1)
+#define LONG_MAX 9223372036854775807L
+#define ULONG_MAX 18446744073709551615UL
+#define LLONG_MIN (-9223372036854775807LL - 1)
+#define LLONG_MAX 9223372036854775807LL
+#define ULLONG_MAX 18446744073709551615ULL
+#define DBL_MAX 1.7976931348623157e+308
+
+#endif
diff --git a/src/userland/libc/locale.c b/src/userland/libc/locale.c
new file mode 100644
index 0000000..4276e7d
--- /dev/null
+++ b/src/userland/libc/locale.c
@@ -0,0 +1,19 @@
+#include "locale.h"
+#include "string.h"
+
+static struct lconv _b_lconv = {
+ ".", "", "", "", "", ".", "", "", "", "",
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+__attribute__((weak)) char *setlocale(int category, const char *locale) {
+ (void)category;
+ if (locale == NULL || strcmp(locale, "C") == 0 || strcmp(locale, "") == 0) {
+ return "C";
+ }
+ return NULL;
+}
+
+__attribute__((weak)) struct lconv *localeconv(void) {
+ return &_b_lconv;
+}
diff --git a/src/userland/libc/locale.h b/src/userland/libc/locale.h
new file mode 100644
index 0000000..46158da
--- /dev/null
+++ b/src/userland/libc/locale.h
@@ -0,0 +1,30 @@
+#ifndef BOREDOS_LIBC_LOCALE_H
+#define BOREDOS_LIBC_LOCALE_H
+
+struct lconv {
+ char *decimal_point;
+ char *thousands_sep;
+ char *grouping;
+ char *int_curr_symbol;
+ char *currency_symbol;
+ char *mon_decimal_point;
+ char *mon_thousands_sep;
+ char *mon_grouping;
+ char *positive_sign;
+ char *negative_sign;
+ char int_frac_digits;
+ char frac_digits;
+ char p_cs_precedes;
+ char p_sep_by_space;
+ char n_cs_precedes;
+ char n_sep_by_space;
+ char p_sign_posn;
+ char n_sign_posn;
+};
+
+#define LC_ALL 0
+
+char *setlocale(int category, const char *locale);
+struct lconv *localeconv(void);
+
+#endif
diff --git a/src/userland/libc/math.h b/src/userland/libc/math.h
index acd193c..25d91ab 100644
--- a/src/userland/libc/math.h
+++ b/src/userland/libc/math.h
@@ -28,7 +28,12 @@ double log(double x);
double log2(double x);
double log10(double x);
double exp(double x);
+double ldexp(double x, int expn);
+double frexp(double x, int *expn);
double pow(double base, double exponent);
+double atan2(double y, double x);
+double asin(double x);
+double acos(double x);
double sinh(double x);
double cosh(double x);
double tanh(double x);
diff --git a/src/userland/libc/math_ext.c b/src/userland/libc/math_ext.c
new file mode 100644
index 0000000..180b6af
--- /dev/null
+++ b/src/userland/libc/math_ext.c
@@ -0,0 +1,91 @@
+#include "math.h"
+#include "errno.h"
+
+__attribute__((weak)) double ldexp(double x, int expn) {
+ double v = x;
+ int i;
+ if (expn >= 0) {
+ for (i = 0; i < expn; i++) {
+ v *= 2.0;
+ }
+ } else {
+ for (i = 0; i < -expn; i++) {
+ v *= 0.5;
+ }
+ }
+ return v;
+}
+
+__attribute__((weak)) double frexp(double x, int *expn) {
+ int e = 0;
+ double v = x;
+ if (x == 0.0) {
+ *expn = 0;
+ return 0.0;
+ }
+ while (fabs(v) >= 1.0) {
+ v *= 0.5;
+ e++;
+ }
+ while (fabs(v) > 0.0 && fabs(v) < 0.5) {
+ v *= 2.0;
+ e--;
+ }
+ *expn = e;
+ return v;
+}
+
+static double _b_atan_series(double x) {
+ double x2 = x * x;
+ double term = x;
+ double sum = x;
+ int n;
+ for (n = 3; n <= 23; n += 2) {
+ term *= -x2;
+ sum += term / (double)n;
+ }
+ return sum;
+}
+
+static double _b_atan_precise(double x) {
+ if (x < 0.0) {
+ return -_b_atan_precise(-x);
+ }
+ if (x > 1.0) {
+ return (M_PI / 2.0) - _b_atan_precise(1.0 / x);
+ }
+ if (x > 0.5) {
+ double y = (x - 1.0) / (x + 1.0);
+ return (M_PI / 4.0) + _b_atan_series(y);
+ }
+ return _b_atan_series(x);
+}
+
+__attribute__((weak)) double atan2(double y, double x) {
+ if (x > 0.0) {
+ return _b_atan_precise(y / x);
+ }
+ if (x < 0.0) {
+ if (y >= 0.0) return _b_atan_precise(y / x) + M_PI;
+ return _b_atan_precise(y / x) - M_PI;
+ }
+ if (y > 0.0) return M_PI / 2.0;
+ if (y < 0.0) return -M_PI / 2.0;
+ return 0.0;
+}
+
+__attribute__((weak)) double asin(double x) {
+ if (x > 1.0 || x < -1.0) {
+ errno = EDOM;
+ return 0.0;
+ }
+ return atan2(x, sqrt(1.0 - x * x));
+}
+
+__attribute__((weak)) double acos(double x) {
+ if (x > 1.0 || x < -1.0) {
+ errno = EDOM;
+ return 0.0;
+ }
+ return M_PI / 2.0 - asin(x);
+}
diff --git a/src/userland/libc/posix_fs.c b/src/userland/libc/posix_fs.c
new file mode 100644
index 0000000..66c877b
--- /dev/null
+++ b/src/userland/libc/posix_fs.c
@@ -0,0 +1,87 @@
+#include "errno.h"
+#include "syscall.h"
+#include "sys/stat.h"
+
+static void _b_stat_init(struct stat *st) {
+ st->st_dev = 0;
+ st->st_ino = 0;
+ st->st_mode = 0;
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ st->st_size = 0;
+ st->st_blksize = 512;
+ st->st_blocks = 0;
+ st->st_atime = 0;
+ st->st_mtime = 0;
+ st->st_ctime = 0;
+}
+
+__attribute__((weak)) int mkdir(const char *pathname, int mode) {
+ (void)mode;
+ if (!pathname || pathname[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sys_exists(pathname)) {
+ errno = EEXIST;
+ return -1;
+ }
+ if (sys_mkdir(pathname) == 0) {
+ return 0;
+ }
+ errno = EIO;
+ return -1;
+}
+
+__attribute__((weak)) int access(const char *pathname, int mode) {
+ (void)mode;
+ if (!pathname || pathname[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sys_exists(pathname)) {
+ return 0;
+ }
+ errno = ENOENT;
+ return -1;
+}
+
+__attribute__((weak)) int stat(const char *pathname, struct stat *statbuf) {
+ FAT32_FileInfo info;
+ if (!pathname || pathname[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!sys_exists(pathname)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (statbuf) {
+ _b_stat_init(statbuf);
+ if (sys_get_file_info(pathname, &info) == 0) {
+ statbuf->st_size = (int)info.size;
+ if (info.is_directory) {
+ statbuf->st_mode = S_IFDIR | 0755;
+ } else {
+ statbuf->st_mode = S_IFREG | 0644;
+ }
+ statbuf->st_blocks = (statbuf->st_size + 511) / 512;
+ } else {
+ int fd = sys_open(pathname, "rb");
+ if (fd >= 0) {
+ statbuf->st_size = (int)sys_size(fd);
+ statbuf->st_mode = S_IFREG | 0644;
+ statbuf->st_blocks = (statbuf->st_size + 511) / 512;
+ sys_close(fd);
+ } else {
+ errno = EIO;
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/userland/libc/posix_io.c b/src/userland/libc/posix_io.c
new file mode 100644
index 0000000..d970590
--- /dev/null
+++ b/src/userland/libc/posix_io.c
@@ -0,0 +1,650 @@
+#include
+#include
+#include
+
+#include "errno.h"
+#include "fcntl.h"
+#include "stdlib.h"
+#include "string.h"
+#include "sys/stat.h"
+#include "syscall.h"
+#include "unistd.h"
+
+#define POSIX_MAX_FDS 256
+#define PIPE_BUF_SIZE 4096
+
+typedef enum {
+ HANDLE_UNUSED = 0,
+ HANDLE_KERNEL_FD,
+ HANDLE_PIPE_READ,
+ HANDLE_PIPE_WRITE
+} handle_type_t;
+
+typedef struct {
+ unsigned char data[PIPE_BUF_SIZE];
+ size_t read_pos;
+ size_t write_pos;
+ size_t count;
+ int readers;
+ int writers;
+} pipe_state_t;
+
+typedef struct {
+ handle_type_t type;
+ int refcount;
+ int flags;
+ int kernel_fd;
+ pipe_state_t *pipe;
+} fd_handle_t;
+
+static fd_handle_t *g_fd_table[POSIX_MAX_FDS];
+static fd_handle_t g_stdio_handles[3];
+static int g_fd_initialized = 0;
+
+static void _b_fd_init(void) {
+ int i;
+ if (g_fd_initialized) {
+ return;
+ }
+ for (i = 0; i < POSIX_MAX_FDS; i++) {
+ g_fd_table[i] = NULL;
+ }
+ for (i = 0; i < 3; i++) {
+ g_stdio_handles[i].type = HANDLE_KERNEL_FD;
+ g_stdio_handles[i].refcount = 1;
+ g_stdio_handles[i].flags = O_RDWR;
+ g_stdio_handles[i].kernel_fd = i;
+ g_stdio_handles[i].pipe = NULL;
+ g_fd_table[i] = &g_stdio_handles[i];
+ }
+ g_fd_initialized = 1;
+}
+
+static int _b_alloc_fd_from(int start) {
+ int fd;
+ for (fd = start; fd < POSIX_MAX_FDS; fd++) {
+ if (g_fd_table[fd] == NULL) {
+ return fd;
+ }
+ }
+ return -1;
+}
+
+static fd_handle_t *_b_get_handle(int fd) {
+ if (fd < 0 || fd >= POSIX_MAX_FDS) {
+ return NULL;
+ }
+ return g_fd_table[fd];
+}
+
+static void _b_reset_stat_common(struct stat *st) {
+ memset(st, 0, sizeof(*st));
+ st->st_blksize = 512;
+}
+
+static int _b_fill_kernel_fstat(int kfd, struct stat *statbuf) {
+ _b_reset_stat_common(statbuf);
+ statbuf->st_mode = (kfd <= 2) ? (S_IFCHR | 0666) : (S_IFREG | 0644);
+ statbuf->st_size = (kfd <= 2) ? 0 : (int)sys_size(kfd);
+ statbuf->st_blocks = (statbuf->st_size + 511) / 512;
+ statbuf->st_nlink = 1;
+ return 0;
+}
+
+static const char *_b_mode_from_flags(int flags) {
+ int accmode = flags & O_ACCMODE;
+
+ if (accmode == O_RDONLY) {
+ return "rb";
+ }
+
+ if (accmode == O_RDWR) {
+ if (flags & O_TRUNC) {
+ return "w+";
+ }
+ if (flags & O_APPEND) {
+ return "a+";
+ }
+ return "r+";
+ }
+
+ if (flags & O_APPEND) {
+ return "ab";
+ }
+ if (flags & O_TRUNC) {
+ return "wb";
+ }
+ return "wb";
+}
+
+static int _b_pipe_read(fd_handle_t *h, void *buf, size_t count) {
+ size_t n = 0;
+ pipe_state_t *p = h->pipe;
+ unsigned char *out = (unsigned char *)buf;
+
+ if (!p || !buf) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (n < count) {
+ if (p->count == 0) {
+ if (p->writers == 0) {
+ break;
+ }
+ if (h->flags & O_NONBLOCK) {
+ if (n == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+ break;
+ }
+ sys_yield();
+ continue;
+ }
+
+ out[n++] = p->data[p->read_pos];
+ p->read_pos = (p->read_pos + 1) % PIPE_BUF_SIZE;
+ p->count--;
+ }
+
+ return (int)n;
+}
+
+static int _b_pipe_write(fd_handle_t *h, const void *buf, size_t count) {
+ size_t n = 0;
+ pipe_state_t *p = h->pipe;
+ const unsigned char *in = (const unsigned char *)buf;
+
+ if (!p || !buf) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (p->readers == 0) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ while (n < count) {
+ if (p->count == PIPE_BUF_SIZE) {
+ if (h->flags & O_NONBLOCK) {
+ if (n == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+ break;
+ }
+ sys_yield();
+ continue;
+ }
+
+ p->data[p->write_pos] = in[n++];
+ p->write_pos = (p->write_pos + 1) % PIPE_BUF_SIZE;
+ p->count++;
+ }
+
+ return (int)n;
+}
+
+__attribute__((weak)) int open(const char *pathname, int flags, ...) {
+ int fd;
+ int kfd;
+ int exists;
+ mode_t mode = 0;
+ fd_handle_t *h;
+
+ _b_fd_init();
+
+ if (!pathname || pathname[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((flags & O_ACCMODE) > O_RDWR) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((flags & O_TRUNC) && ((flags & O_ACCMODE) == O_RDONLY)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ exists = sys_exists(pathname);
+
+ if ((flags & O_CREAT) && (flags & O_EXCL) && exists) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ if (!(flags & O_CREAT) && !exists) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (flags & O_CREAT) {
+ va_list ap;
+ va_start(ap, flags);
+ mode = (mode_t)va_arg(ap, int);
+ va_end(ap);
+ (void)mode;
+ }
+
+ kfd = sys_open(pathname, _b_mode_from_flags(flags));
+ if (kfd < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ h = (fd_handle_t *)malloc(sizeof(fd_handle_t));
+ if (!h) {
+ sys_close(kfd);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ fd = _b_alloc_fd_from(3);
+ if (fd < 0) {
+ free(h);
+ sys_close(kfd);
+ errno = EBUSY;
+ return -1;
+ }
+
+ h->type = HANDLE_KERNEL_FD;
+ h->refcount = 1;
+ h->flags = flags;
+ h->kernel_fd = kfd;
+ h->pipe = NULL;
+ g_fd_table[fd] = h;
+
+ if (flags & O_APPEND) {
+ (void)sys_seek(kfd, 0, SEEK_END);
+ }
+
+ return fd;
+}
+
+__attribute__((weak)) int close(int fd) {
+ fd_handle_t *h;
+
+ _b_fd_init();
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ g_fd_table[fd] = NULL;
+ if (--h->refcount > 0) {
+ return 0;
+ }
+
+ if (h->type == HANDLE_KERNEL_FD) {
+ if (h->kernel_fd >= 3) {
+ sys_close(h->kernel_fd);
+ }
+ } else if (h->type == HANDLE_PIPE_READ || h->type == HANDLE_PIPE_WRITE) {
+ pipe_state_t *p = h->pipe;
+ if (p) {
+ if (h->type == HANDLE_PIPE_READ) {
+ p->readers--;
+ } else {
+ p->writers--;
+ }
+ if (p->readers <= 0 && p->writers <= 0) {
+ free(p);
+ }
+ }
+ }
+
+ if (h < &g_stdio_handles[0] || h > &g_stdio_handles[2]) {
+ free(h);
+ }
+ return 0;
+}
+
+__attribute__((weak)) ssize_t read(int fd, void *buf, size_t count) {
+ fd_handle_t *h;
+ int n;
+
+ _b_fd_init();
+
+ if (!buf && count != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (h->type == HANDLE_PIPE_WRITE) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (h->type == HANDLE_PIPE_READ) {
+ n = _b_pipe_read(h, buf, count);
+ return (ssize_t)n;
+ }
+
+ n = sys_read(h->kernel_fd, buf, (uint32_t)count);
+ if (n < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return (ssize_t)n;
+}
+
+__attribute__((weak)) ssize_t write(int fd, const void *buf, size_t count) {
+ fd_handle_t *h;
+ int n;
+
+ _b_fd_init();
+ if (!buf && count != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (h->type == HANDLE_PIPE_READ) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (h->type == HANDLE_PIPE_WRITE) {
+ n = _b_pipe_write(h, buf, count);
+ return (ssize_t)n;
+ }
+
+ if (h->kernel_fd <= 2) {
+ n = sys_write(h->kernel_fd, (const char *)buf, (int)count);
+ } else {
+ n = sys_write_fs(h->kernel_fd, buf, (uint32_t)count);
+ }
+
+ if (n < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return (ssize_t)n;
+}
+
+__attribute__((weak)) off_t lseek(int fd, off_t offset, int whence) {
+ fd_handle_t *h;
+
+ _b_fd_init();
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+ if (h->type != HANDLE_KERNEL_FD) {
+ errno = ESPIPE;
+ return -1;
+ }
+ if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sys_seek(h->kernel_fd, (int)offset, whence) < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return (off_t)sys_tell(h->kernel_fd);
+}
+
+__attribute__((weak)) int unlink(const char *pathname) {
+ if (!pathname || pathname[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sys_delete(pathname) != 0) {
+ errno = ENOENT;
+ return -1;
+ }
+ return 0;
+}
+
+__attribute__((weak)) int isatty(int fd) {
+ fd_handle_t *h;
+ _b_fd_init();
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return 0;
+ }
+ return (h->type == HANDLE_KERNEL_FD && h->kernel_fd <= 2) ? 1 : 0;
+}
+
+__attribute__((weak)) int fstat(int fd, struct stat *statbuf) {
+ fd_handle_t *h;
+ _b_fd_init();
+ if (!statbuf) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (h->type == HANDLE_PIPE_READ || h->type == HANDLE_PIPE_WRITE) {
+ _b_reset_stat_common(statbuf);
+ statbuf->st_mode = S_IFIFO | 0666;
+ statbuf->st_size = (int)(h->pipe ? h->pipe->count : 0);
+ return 0;
+ }
+
+ return _b_fill_kernel_fstat(h->kernel_fd, statbuf);
+}
+
+__attribute__((weak)) int dup(int oldfd) {
+ fd_handle_t *h;
+ int newfd;
+ int newkfd;
+ _b_fd_init();
+
+ h = _b_get_handle(oldfd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (h->type != HANDLE_KERNEL_FD) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ newkfd = sys_dup(h->kernel_fd);
+ if (newkfd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ newfd = _b_alloc_fd_from(0);
+ if (newfd < 0) {
+ sys_close(newkfd);
+ errno = EBUSY;
+ return -1;
+ }
+
+ h = (fd_handle_t *)malloc(sizeof(fd_handle_t));
+ if (!h) {
+ sys_close(newkfd);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ h->type = HANDLE_KERNEL_FD;
+ h->refcount = 1;
+ h->flags = O_RDWR;
+ h->kernel_fd = newkfd;
+ h->pipe = NULL;
+ g_fd_table[newfd] = h;
+ return newfd;
+}
+
+__attribute__((weak)) int dup2(int oldfd, int newfd) {
+ fd_handle_t *h;
+ fd_handle_t *nh;
+ int newkfd;
+ _b_fd_init();
+
+ h = _b_get_handle(oldfd);
+ if (!h || newfd < 0 || newfd >= POSIX_MAX_FDS) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (oldfd == newfd) {
+ return newfd;
+ }
+
+ if (h->type != HANDLE_KERNEL_FD) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if (g_fd_table[newfd]) {
+ if (close(newfd) != 0) {
+ return -1;
+ }
+ }
+
+ newkfd = sys_dup(h->kernel_fd);
+ if (newkfd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+
+ nh = (fd_handle_t *)malloc(sizeof(fd_handle_t));
+ if (!nh) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ nh->type = HANDLE_KERNEL_FD;
+ nh->refcount = 1;
+ nh->flags = h->flags;
+ nh->kernel_fd = newkfd;
+ nh->pipe = NULL;
+
+ g_fd_table[newfd] = nh;
+ return newfd;
+}
+
+__attribute__((weak)) int pipe(int pipefd[2]) {
+ fd_handle_t *rh;
+ fd_handle_t *wh;
+ int rfd;
+ int wfd;
+ int kpipe[2];
+
+ _b_fd_init();
+ if (!pipefd) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sys_pipe(kpipe) < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ rfd = _b_alloc_fd_from(3);
+ if (rfd < 0) {
+ sys_close(kpipe[0]);
+ sys_close(kpipe[1]);
+ errno = EBUSY;
+ return -1;
+ }
+ wfd = _b_alloc_fd_from(rfd + 1);
+ if (wfd < 0) {
+ sys_close(kpipe[0]);
+ sys_close(kpipe[1]);
+ errno = EBUSY;
+ return -1;
+ }
+
+ rh = (fd_handle_t *)malloc(sizeof(fd_handle_t));
+ wh = (fd_handle_t *)malloc(sizeof(fd_handle_t));
+ if (!rh || !wh) {
+ free(rh);
+ free(wh);
+ sys_close(kpipe[0]);
+ sys_close(kpipe[1]);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ rh->type = HANDLE_KERNEL_FD;
+ rh->refcount = 1;
+ rh->flags = O_RDONLY;
+ rh->kernel_fd = kpipe[0];
+ rh->pipe = NULL;
+
+ wh->type = HANDLE_KERNEL_FD;
+ wh->refcount = 1;
+ wh->flags = O_WRONLY;
+ wh->kernel_fd = kpipe[1];
+ wh->pipe = NULL;
+
+ g_fd_table[rfd] = rh;
+ g_fd_table[wfd] = wh;
+ pipefd[0] = rfd;
+ pipefd[1] = wfd;
+ return 0;
+}
+
+__attribute__((weak)) int fcntl(int fd, int cmd, ...) {
+ fd_handle_t *h;
+ va_list ap;
+ int val;
+
+ _b_fd_init();
+ h = _b_get_handle(fd);
+ if (!h) {
+ errno = EBADF;
+ return -1;
+ }
+
+ switch (cmd) {
+ case F_GETFL:
+ if (h->type == HANDLE_KERNEL_FD) {
+ int k = sys_fcntl(h->kernel_fd, cmd, 0);
+ if (k < 0) {
+ errno = ENOSYS;
+ return -1;
+ }
+ h->flags = k;
+ }
+ return h->flags;
+ case F_SETFL:
+ va_start(ap, cmd);
+ val = va_arg(ap, int);
+ va_end(ap);
+ if (h->type == HANDLE_KERNEL_FD) {
+ if (sys_fcntl(h->kernel_fd, cmd, val) < 0) {
+ errno = ENOSYS;
+ return -1;
+ }
+ }
+ h->flags = (h->flags & ~(O_APPEND | O_NONBLOCK)) | (val & (O_APPEND | O_NONBLOCK));
+ return 0;
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+}
diff --git a/src/userland/libc/posix_process.c b/src/userland/libc/posix_process.c
new file mode 100644
index 0000000..0d15f05
--- /dev/null
+++ b/src/userland/libc/posix_process.c
@@ -0,0 +1,224 @@
+#include
+#include
+
+#include "errno.h"
+#include "stdio.h"
+#include "string.h"
+#include "sys/types.h"
+#include "sys/wait.h"
+#include "syscall.h"
+#include "unistd.h"
+
+static int _b_is_space(char c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+static int _b_path_exists(const char *path) {
+ return path && path[0] && sys_exists(path);
+}
+
+static const char *_b_resolve_exec_path(const char *file, char *out, size_t cap) {
+ if (!file || !file[0]) {
+ return NULL;
+ }
+
+ if (file[0] == '/') {
+ return _b_path_exists(file) ? file : NULL;
+ }
+
+ snprintf(out, cap, "/bin/%s", file);
+ if (_b_path_exists(out)) {
+ return out;
+ }
+
+ snprintf(out, cap, "/bin/%s.elf", file);
+ if (_b_path_exists(out)) {
+ return out;
+ }
+
+ return NULL;
+}
+
+static int _b_join_argv(char *buf, size_t cap, char *const argv[]) {
+ size_t used = 0;
+
+ if (!argv || !argv[0]) {
+ if (cap) {
+ buf[0] = '\0';
+ }
+ return 0;
+ }
+
+ for (int i = 1; argv[i]; i++) {
+ const char *a = argv[i];
+ size_t len = strlen(a);
+ int need_quote = 0;
+
+ for (size_t j = 0; j < len; j++) {
+ if (_b_is_space(a[j]) || a[j] == '"') {
+ need_quote = 1;
+ break;
+ }
+ }
+
+ if (used && used + 1 < cap) {
+ buf[used++] = ' ';
+ }
+
+ if (need_quote && used + 1 < cap) {
+ buf[used++] = '"';
+ }
+
+ for (size_t j = 0; j < len; j++) {
+ if (a[j] == '"' && used + 2 < cap) {
+ buf[used++] = '\\';
+ }
+ if (used + 1 < cap) {
+ buf[used++] = a[j];
+ }
+ }
+
+ if (need_quote && used + 1 < cap) {
+ buf[used++] = '"';
+ }
+
+ if (used >= cap) {
+ errno = E2BIG;
+ return -1;
+ }
+ }
+
+ if (cap) {
+ buf[used < cap ? used : cap - 1] = '\0';
+ }
+ return 0;
+}
+
+static int _b_exec_common(const char *path, char *const argv[]) {
+ char resolved[260];
+ char args[512];
+ const char *exec_path = _b_resolve_exec_path(path, resolved, sizeof(resolved));
+
+ if (!exec_path) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (_b_join_argv(args, sizeof(args), argv) != 0) {
+ return -1;
+ }
+
+ if (sys_exec(exec_path, args[0] ? args : NULL) < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+__attribute__((weak)) int execv(const char *path, char *const argv[]) {
+ return _b_exec_common(path, argv);
+}
+
+__attribute__((weak)) int execve(const char *path, char *const argv[], char *const envp[]) {
+ (void)envp;
+ return _b_exec_common(path, argv);
+}
+
+__attribute__((weak)) int execvp(const char *file, char *const argv[]) {
+ return _b_exec_common(file, argv);
+}
+
+__attribute__((weak)) int execl(const char *path, const char *arg, ...) {
+ va_list ap;
+ char *argv[64];
+ int i = 0;
+
+ argv[i++] = (char *)arg;
+ va_start(ap, arg);
+ while (i < 63) {
+ char *v = va_arg(ap, char *);
+ argv[i++] = v;
+ if (!v) {
+ break;
+ }
+ }
+ va_end(ap);
+
+ if (argv[i - 1] != NULL) {
+ argv[63] = NULL;
+ }
+
+ return execv(path, argv);
+}
+
+__attribute__((weak)) int execlp(const char *file, const char *arg, ...) {
+ va_list ap;
+ char *argv[64];
+ int i = 0;
+
+ argv[i++] = (char *)arg;
+ va_start(ap, arg);
+ while (i < 63) {
+ char *v = va_arg(ap, char *);
+ argv[i++] = v;
+ if (!v) {
+ break;
+ }
+ }
+ va_end(ap);
+
+ if (argv[i - 1] != NULL) {
+ argv[63] = NULL;
+ }
+
+ return execvp(file, argv);
+}
+
+__attribute__((weak)) int execle(const char *path, const char *arg, ...) {
+ va_list ap;
+ char *argv[64];
+ int i = 0;
+ char *envp;
+
+ argv[i++] = (char *)arg;
+ va_start(ap, arg);
+ while (i < 63) {
+ char *v = va_arg(ap, char *);
+ argv[i++] = v;
+ if (!v) {
+ break;
+ }
+ }
+ envp = va_arg(ap, char *);
+ va_end(ap);
+ (void)envp;
+
+ if (argv[i - 1] != NULL) {
+ argv[63] = NULL;
+ }
+
+ return execv(path, argv);
+}
+
+__attribute__((weak)) pid_t waitpid(pid_t pid, int *status, int options) {
+ int st = 0;
+
+ for (;;) {
+ int rc = sys_waitpid((int)pid, &st, options);
+ if (rc > 0) {
+ if (status) {
+ *status = st;
+ }
+ return (pid_t)rc;
+ }
+ if (rc == 0 && (options & WNOHANG)) {
+ return 0;
+ }
+ if (rc < 0) {
+ errno = ECHILD;
+ return -1;
+ }
+ sys_yield();
+ }
+}
diff --git a/src/userland/libc/runtime.c b/src/userland/libc/runtime.c
new file mode 100644
index 0000000..9d21b32
--- /dev/null
+++ b/src/userland/libc/runtime.c
@@ -0,0 +1,178 @@
+#include "stdlib.h"
+#include "string.h"
+#include "errno.h"
+#include "syscall.h"
+#include "stdio.h"
+#include "math.h"
+
+static int _b_is_space_char(int c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+static int _b_try_spawn_command(const char *command) {
+ char cmd[128];
+ char args[256];
+ int i = 0;
+ int j = 0;
+ int pid;
+
+ while (command[i] && _b_is_space_char((unsigned char)command[i])) i++;
+ while (command[i] && !_b_is_space_char((unsigned char)command[i]) && j < (int)sizeof(cmd) - 1) {
+ cmd[j++] = command[i++];
+ }
+ cmd[j] = '\0';
+
+ if (cmd[0] == '\0') {
+ return 0;
+ }
+
+ while (command[i] && _b_is_space_char((unsigned char)command[i])) i++;
+ {
+ int k = 0;
+ while (command[i] && k < (int)sizeof(args) - 1) {
+ args[k++] = command[i++];
+ }
+ args[k] = '\0';
+ }
+
+ pid = sys_spawn(cmd, args[0] ? args : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0);
+ if (pid >= 0) {
+ return 0;
+ }
+
+ if (cmd[0] != '/') {
+ char path[160];
+ snprintf(path, sizeof(path), "/bin/%s", cmd);
+ pid = sys_spawn(path, args[0] ? args : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0);
+ if (pid >= 0) {
+ return 0;
+ }
+
+ snprintf(path, sizeof(path), "/bin/%s.elf", cmd);
+ pid = sys_spawn(path, args[0] ? args : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0);
+ if (pid >= 0) {
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+static int _b_streq(const char *a, const char *b) {
+ while (*a && *b) {
+ if (*a != *b) {
+ return 0;
+ }
+ a++;
+ b++;
+ }
+ return *a == '\0' && *b == '\0';
+}
+
+__attribute__((weak)) int abs(int x) {
+ return (x < 0) ? -x : x;
+}
+
+__attribute__((weak)) int system(const char *command) {
+ if (command == NULL) {
+ return 1;
+ }
+ return _b_try_spawn_command(command);
+}
+
+__attribute__((weak)) char *getenv(const char *name) {
+ const char *cfg;
+ static char cwd_buf[256];
+ if (!name || name[0] == '\0') {
+ return NULL;
+ }
+
+ if (_b_streq(name, "PWD")) {
+ if (sys_getcwd(cwd_buf, (int)sizeof(cwd_buf)) >= 0) {
+ return cwd_buf;
+ }
+ return NULL;
+ }
+
+ if (_b_streq(name, "PATH")) return "/bin:/";
+ if (_b_streq(name, "HOME")) return "/";
+ if (_b_streq(name, "SHELL")) return "/bin/bsh";
+ if (_b_streq(name, "TERM")) return "boredos";
+
+ cfg = (const char *)(uintptr_t)sys_get_shell_config(name);
+ if ((uintptr_t)cfg > 0x10000ULL) {
+ return (char *)cfg;
+ }
+ return NULL;
+}
+
+__attribute__((weak)) void abort(void) {
+ sys_exit(1);
+ while (1) {}
+}
+
+__attribute__((weak)) void _exit(int status) {
+ sys_exit(status);
+ while (1) {}
+}
+
+__attribute__((weak)) double strtod(const char *nptr, char **endptr) {
+ const char *p = nptr;
+ int sign = 1;
+ double value = 0.0;
+ double frac = 0.0;
+ double scale = 1.0;
+ int exp_sign = 1;
+ int exp_val = 0;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v') p++;
+
+ if (*p == '-') {
+ sign = -1;
+ p++;
+ } else if (*p == '+') {
+ p++;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ value = value * 10.0 + (double)(*p - '0');
+ p++;
+ }
+
+ if (*p == '.') {
+ p++;
+ while (*p >= '0' && *p <= '9') {
+ frac = frac * 10.0 + (double)(*p - '0');
+ scale *= 10.0;
+ p++;
+ }
+ value += frac / scale;
+ }
+
+ if (*p == 'e' || *p == 'E') {
+ const char *ep = p + 1;
+ if (*ep == '-') {
+ exp_sign = -1;
+ ep++;
+ } else if (*ep == '+') {
+ ep++;
+ }
+ if (*ep >= '0' && *ep <= '9') {
+ p = ep;
+ while (*p >= '0' && *p <= '9') {
+ exp_val = exp_val * 10 + (*p - '0');
+ p++;
+ }
+ }
+ }
+
+ if (endptr) {
+ *endptr = (char *)p;
+ }
+
+ if (exp_val != 0) {
+ value = ldexp(value, exp_sign * exp_val);
+ }
+ return sign * value;
+}
diff --git a/src/userland/libc/setjmp.c b/src/userland/libc/setjmp.c
new file mode 100644
index 0000000..9a57aa8
--- /dev/null
+++ b/src/userland/libc/setjmp.c
@@ -0,0 +1,38 @@
+#include "setjmp.h"
+
+__attribute__((weak)) int setjmp(jmp_buf env) {
+ __asm__ volatile(
+ "movq %%rbx, 0(%0)\n\t"
+ "movq %%rbp, 8(%0)\n\t"
+ "movq %%r12, 16(%0)\n\t"
+ "movq %%r13, 24(%0)\n\t"
+ "movq %%r14, 32(%0)\n\t"
+ "movq %%r15, 40(%0)\n\t"
+ "leaq 8(%%rsp), %%rax\n\t"
+ "movq %%rax, 48(%0)\n\t"
+ "movq (%%rsp), %%rax\n\t"
+ "movq %%rax, 56(%0)\n\t"
+ :
+ : "r"(env)
+ : "rax", "memory");
+ return 0;
+}
+
+__attribute__((weak)) void longjmp(jmp_buf env, int val) {
+ int r = (val == 0) ? 1 : val;
+ __asm__ volatile(
+ "movq 0(%0), %%rbx\n\t"
+ "movq 8(%0), %%rbp\n\t"
+ "movq 16(%0), %%r12\n\t"
+ "movq 24(%0), %%r13\n\t"
+ "movq 32(%0), %%r14\n\t"
+ "movq 40(%0), %%r15\n\t"
+ "movq 48(%0), %%rsp\n\t"
+ "movl %1, %%eax\n\t"
+ "movq 56(%0), %%rdx\n\t"
+ "jmp *%%rdx\n\t"
+ :
+ : "r"(env), "r"(r)
+ : "rax", "rdx", "memory");
+ __builtin_unreachable();
+}
diff --git a/src/userland/libc/setjmp.h b/src/userland/libc/setjmp.h
new file mode 100644
index 0000000..4bbc400
--- /dev/null
+++ b/src/userland/libc/setjmp.h
@@ -0,0 +1,20 @@
+#ifndef BOREDOS_LIBC_SETJMP_H
+#define BOREDOS_LIBC_SETJMP_H
+
+#include
+
+typedef struct boredos_jmp_buf_s {
+ uint64_t rbx;
+ uint64_t rbp;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rsp;
+ uint64_t rip;
+} jmp_buf[1];
+
+int setjmp(jmp_buf env) __attribute__((returns_twice, noinline));
+void longjmp(jmp_buf env, int val) __attribute__((noreturn, noinline));
+
+#endif
diff --git a/src/userland/libc/signal.c b/src/userland/libc/signal.c
new file mode 100644
index 0000000..8c3b95c
--- /dev/null
+++ b/src/userland/libc/signal.c
@@ -0,0 +1,108 @@
+#include "signal.h"
+#include "errno.h"
+#include "stdlib.h"
+#include "syscall.h"
+
+typedef struct {
+ unsigned long sa_handler;
+ unsigned long sa_mask;
+ int sa_flags;
+} k_sigaction_t;
+
+__attribute__((weak)) sighandler_t signal(int sig, sighandler_t handler) {
+ struct sigaction act;
+ struct sigaction old;
+
+ if (sig <= 0 || sig >= 32) {
+ errno = EINVAL;
+ return SIG_ERR;
+ }
+
+ act.sa_handler = handler;
+ act.sa_mask = 0;
+ act.sa_flags = 0;
+ if (sigaction(sig, &act, &old) != 0) {
+ return SIG_ERR;
+ }
+ return old.sa_handler;
+}
+
+__attribute__((weak)) int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) {
+ k_sigaction_t kact;
+ k_sigaction_t kold;
+ int rc;
+
+ if (sig <= 0 || sig >= 32) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (act) {
+ kact.sa_handler = (unsigned long)act->sa_handler;
+ kact.sa_mask = (unsigned long)act->sa_mask;
+ kact.sa_flags = act->sa_flags;
+ }
+
+ rc = sys_sigaction(sig, act ? &kact : NULL, oldact ? &kold : NULL);
+ if (rc < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (oldact) {
+ oldact->sa_handler = (sighandler_t)kold.sa_handler;
+ oldact->sa_mask = (sigset_t)kold.sa_mask;
+ oldact->sa_flags = kold.sa_flags;
+ }
+
+ return 0;
+}
+
+__attribute__((weak)) int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) {
+ if (sys_sigprocmask(how,
+ (const unsigned long *)set,
+ (unsigned long *)oldset) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+__attribute__((weak)) int sigpending(sigset_t *set) {
+ if (!set) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (sys_sigpending((unsigned long *)set) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+__attribute__((weak)) int raise(int sig) {
+ if (sig <= 0 || sig >= 32) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sys_kill_signal(-1, sig) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+__attribute__((weak)) int kill(pid_t pid, int sig) {
+ if (pid <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sys_kill_signal((int)pid, sig) < 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/userland/libc/signal.h b/src/userland/libc/signal.h
new file mode 100644
index 0000000..671f8c1
--- /dev/null
+++ b/src/userland/libc/signal.h
@@ -0,0 +1,37 @@
+#ifndef BOREDOS_LIBC_SIGNAL_H
+#define BOREDOS_LIBC_SIGNAL_H
+
+#include "sys/types.h"
+
+typedef void (*sighandler_t)(int);
+typedef unsigned long sigset_t;
+
+struct sigaction {
+ sighandler_t sa_handler;
+ sigset_t sa_mask;
+ int sa_flags;
+};
+
+#define SIG_DFL ((sighandler_t)0)
+#define SIG_IGN ((sighandler_t)1)
+#define SIG_ERR ((sighandler_t)-1)
+#define SIGKILL 9
+#define SIGTERM 15
+#define SIGINT 2
+
+#define SIG_BLOCK 0
+#define SIG_UNBLOCK 1
+#define SIG_SETMASK 2
+
+#define SA_RESTART 0x10000000
+#define SA_NODEFER 0x40000000
+#define SA_RESETHAND 0x80000000
+
+sighandler_t signal(int sig, sighandler_t handler);
+int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
+int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
+int sigpending(sigset_t *set);
+int raise(int sig);
+int kill(pid_t pid, int sig);
+
+#endif
diff --git a/src/userland/libc/stdio.c b/src/userland/libc/stdio.c
new file mode 100644
index 0000000..510d589
--- /dev/null
+++ b/src/userland/libc/stdio.c
@@ -0,0 +1,1046 @@
+#include
+#include
+#include
+
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "ctype.h"
+#include "errno.h"
+#include "syscall.h"
+#include "time.h"
+
+static FILE boredos_stdin_obj = {0, 0, 0, 0, 0};
+static FILE boredos_stdout_obj = {1, 0, 0, 0, 0};
+static FILE boredos_stderr_obj = {2, 0, 0, 0, 0};
+__attribute__((weak)) FILE *stdin = &boredos_stdin_obj;
+__attribute__((weak)) FILE *stdout = &boredos_stdout_obj;
+__attribute__((weak)) FILE *stderr = &boredos_stderr_obj;
+
+static int _b_streq(const char *a, const char *b) {
+ while (*a && *b) {
+ if (*a != *b) {
+ return 0;
+ }
+ a++;
+ b++;
+ }
+ return *a == '\0' && *b == '\0';
+}
+
+static int _b_is_space_char(int c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+static char _b_tmpname_buf[FILENAME_MAX];
+static unsigned _b_tmp_counter = 0;
+
+static char *_b_tmpname_generate(char *out, size_t out_cap) {
+ unsigned tries;
+ if (out_cap == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ (void)sys_mkdir("/tmp");
+ for (tries = 0; tries < 256; tries++) {
+ unsigned long long t = (unsigned long long)clock();
+ snprintf(out, out_cap, "/tmp/tmp_%llu_%u.tmp", t, _b_tmp_counter++);
+ if (!sys_exists(out)) {
+ return out;
+ }
+ }
+
+ errno = EEXIST;
+ return NULL;
+}
+
+__attribute__((weak)) FILE *fopen(const char *path, const char *mode) {
+ int fd = sys_open(path, mode);
+ FILE *f;
+ if (fd < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ f = (FILE *)malloc(sizeof(FILE));
+ if (!f) {
+ sys_close(fd);
+ errno = ENOMEM;
+ return NULL;
+ }
+ f->fd = fd;
+ f->eof = 0;
+ f->err = 0;
+ f->has_ungetc = 0;
+ f->ungetc_char = 0;
+ return f;
+}
+
+__attribute__((weak)) FILE *freopen(const char *path, const char *mode, FILE *stream) {
+ int fd;
+ if (!stream) {
+ return fopen(path, mode);
+ }
+ if (stream->fd >= 0) {
+ sys_close(stream->fd);
+ }
+ fd = sys_open(path, mode);
+ if (fd < 0) {
+ stream->err = 1;
+ errno = EINVAL;
+ return NULL;
+ }
+ stream->fd = fd;
+ stream->eof = 0;
+ stream->err = 0;
+ stream->has_ungetc = 0;
+ return stream;
+}
+
+__attribute__((weak)) int fclose(FILE *stream) {
+ if (!stream) {
+ return EOF;
+ }
+ if (stream != stdin && stream != stdout && stream != stderr) {
+ if (stream->fd >= 0) {
+ sys_close(stream->fd);
+ }
+ free(stream);
+ }
+ return 0;
+}
+
+__attribute__((weak)) size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ size_t total;
+ int n;
+ if (!stream || !ptr || size == 0 || nmemb == 0) {
+ return 0;
+ }
+ total = size * nmemb;
+ n = sys_read(stream->fd, ptr, (uint32_t)total);
+ if (n <= 0) {
+ if (n == 0) {
+ stream->eof = 1;
+ } else {
+ stream->err = 1;
+ }
+ return 0;
+ }
+ if ((size_t)n < total) {
+ stream->eof = 1;
+ }
+ return (size_t)n / size;
+}
+
+__attribute__((weak)) size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ size_t total;
+ int n;
+ if (!stream || !ptr || size == 0 || nmemb == 0) {
+ return 0;
+ }
+ total = size * nmemb;
+ if (stream->fd <= 2) {
+ n = sys_write(stream->fd, (const char *)ptr, (int)total);
+ } else {
+ n = sys_write_fs(stream->fd, ptr, (uint32_t)total);
+ }
+ if (n < 0) {
+ stream->err = 1;
+ return 0;
+ }
+ return (size_t)n / size;
+}
+
+__attribute__((weak)) int fseek(FILE *stream, long offset, int whence) {
+ if (!stream) {
+ return -1;
+ }
+ if (sys_seek(stream->fd, (int)offset, whence) < 0) {
+ stream->err = 1;
+ return -1;
+ }
+ stream->eof = 0;
+ stream->has_ungetc = 0;
+ return 0;
+}
+
+__attribute__((weak)) long ftell(FILE *stream) {
+ if (!stream) {
+ return -1;
+ }
+ return (long)sys_tell(stream->fd);
+}
+
+__attribute__((weak)) int getc(FILE *stream) {
+ unsigned char ch;
+ int n;
+ if (!stream) {
+ return EOF;
+ }
+ if (stream->has_ungetc) {
+ stream->has_ungetc = 0;
+ return stream->ungetc_char;
+ }
+ n = sys_read(stream->fd, &ch, 1);
+ if (n <= 0) {
+ if (n == 0) {
+ stream->eof = 1;
+ } else {
+ stream->err = 1;
+ }
+ return EOF;
+ }
+ return (int)ch;
+}
+
+__attribute__((weak)) int ungetc(int c, FILE *stream) {
+ if (!stream || c == EOF) {
+ return EOF;
+ }
+ stream->has_ungetc = 1;
+ stream->ungetc_char = (unsigned char)c;
+ stream->eof = 0;
+ return c;
+}
+
+__attribute__((weak)) char *fgets(char *s, int n, FILE *stream) {
+ int i;
+ if (!s || n <= 0 || !stream) {
+ return NULL;
+ }
+ for (i = 0; i < n - 1; i++) {
+ int c = getc(stream);
+ if (c == EOF) {
+ break;
+ }
+ s[i] = (char)c;
+ if (c == '\n') {
+ i++;
+ break;
+ }
+ }
+ if (i == 0) {
+ return NULL;
+ }
+ s[i] = '\0';
+ return s;
+}
+
+__attribute__((weak)) int fputs(const char *s, FILE *stream) {
+ size_t len;
+ size_t written;
+ if (!s || !stream) {
+ return EOF;
+ }
+ len = strlen(s);
+ written = fwrite(s, 1, len, stream);
+ return (written == len) ? (int)len : EOF;
+}
+
+__attribute__((weak)) int feof(FILE *stream) {
+ return stream ? stream->eof : 1;
+}
+
+__attribute__((weak)) int ferror(FILE *stream) {
+ return stream ? stream->err : 1;
+}
+
+__attribute__((weak)) void clearerr(FILE *stream) {
+ if (stream) {
+ stream->eof = 0;
+ stream->err = 0;
+ }
+}
+
+__attribute__((weak)) int fflush(FILE *stream) {
+ (void)stream;
+ return 0;
+}
+
+__attribute__((weak)) int remove(const char *path) {
+ return sys_delete(path);
+}
+
+__attribute__((weak)) int rename(const char *oldpath, const char *newpath) {
+ FILE *src;
+ FILE *dst;
+ char buf[1024];
+ size_t nread;
+
+ if (!oldpath || !newpath || oldpath[0] == '\0' || newpath[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_b_streq(oldpath, newpath)) {
+ return 0;
+ }
+
+ src = fopen(oldpath, "rb");
+ if (!src) {
+ errno = ENOENT;
+ return -1;
+ }
+ dst = fopen(newpath, "wb");
+ if (!dst) {
+ fclose(src);
+ return -1;
+ }
+
+ for (;;) {
+ nread = fread(buf, 1, sizeof(buf), src);
+ if (nread == 0) {
+ break;
+ }
+ if (fwrite(buf, 1, nread, dst) != nread) {
+ fclose(src);
+ fclose(dst);
+ sys_delete(newpath);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ if (ferror(src) || ferror(dst)) {
+ fclose(src);
+ fclose(dst);
+ sys_delete(newpath);
+ errno = EIO;
+ return -1;
+ }
+
+ fclose(src);
+ fclose(dst);
+ if (sys_delete(oldpath) != 0) {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+__attribute__((weak)) FILE *tmpfile(void) {
+ char path[FILENAME_MAX];
+ if (!_b_tmpname_generate(path, sizeof(path))) {
+ return NULL;
+ }
+ return fopen(path, "w+");
+}
+
+__attribute__((weak)) char *tmpnam(char *s) {
+ char *dst = s ? s : _b_tmpname_buf;
+ return _b_tmpname_generate(dst, FILENAME_MAX);
+}
+
+static int _b_hex_digit(unsigned value, int upper) {
+ if (value < 10U) {
+ return (int)('0' + value);
+ }
+ return (int)((upper ? 'A' : 'a') + (value - 10U));
+}
+
+static void _b_append_char(char *out, size_t cap, size_t *idx, int c) {
+ if (*idx + 1 < cap) {
+ out[*idx] = (char)c;
+ }
+ (*idx)++;
+}
+
+static void _b_append_strn(char *out, size_t cap, size_t *idx, const char *s, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ _b_append_char(out, cap, idx, s[i]);
+ }
+}
+
+static void _b_append_repeat(char *out, size_t cap, size_t *idx, char ch, int count) {
+ int i;
+ for (i = 0; i < count; i++) {
+ _b_append_char(out, cap, idx, ch);
+ }
+}
+
+static void _b_utoa(unsigned long long v, unsigned base, int upper, char *buf, size_t *len) {
+ char tmp[64];
+ size_t i = 0;
+ if (v == 0) {
+ tmp[i++] = '0';
+ } else {
+ while (v && i < sizeof(tmp)) {
+ tmp[i++] = (char)_b_hex_digit((unsigned)(v % base), upper);
+ v /= base;
+ }
+ }
+ *len = i;
+ while (i > 0) {
+ *buf++ = tmp[--i];
+ }
+}
+
+static void _b_itoa(long long v, char *buf, size_t *len) {
+ unsigned long long uv;
+ size_t n = 0;
+ if (v < 0) {
+ *buf++ = '-';
+ n++;
+ uv = (unsigned long long)(-(v + 1)) + 1ULL;
+ } else {
+ uv = (unsigned long long)v;
+ }
+ _b_utoa(uv, 10U, 0, buf, len);
+ *len += n;
+}
+
+static void _b_ftoa(double d, int precision, char *buf, size_t *len) {
+ long long ip;
+ double frac;
+ size_t n = 0;
+ if (precision < 0) {
+ precision = 6;
+ }
+ if (d < 0.0) {
+ buf[n++] = '-';
+ d = -d;
+ }
+ ip = (long long)d;
+ frac = d - (double)ip;
+ {
+ char ibuf[64];
+ size_t ilen = 0;
+ _b_utoa((unsigned long long)ip, 10U, 0, ibuf, &ilen);
+ memcpy(buf + n, ibuf, ilen);
+ n += ilen;
+ }
+ if (precision > 0) {
+ int i;
+ buf[n++] = '.';
+ for (i = 0; i < precision; i++) {
+ int digit;
+ frac *= 10.0;
+ digit = (int)frac;
+ if (digit < 0) digit = 0;
+ if (digit > 9) digit = 9;
+ buf[n++] = (char)('0' + digit);
+ frac -= (double)digit;
+ }
+ }
+ *len = n;
+}
+
+__attribute__((weak)) int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
+ size_t out_i = 0;
+
+ while (*fmt) {
+ if (*fmt != '%') {
+ _b_append_char(str, size, &out_i, *fmt++);
+ continue;
+ }
+
+ fmt++;
+ if (*fmt == '%') {
+ _b_append_char(str, size, &out_i, '%');
+ fmt++;
+ continue;
+ }
+
+ {
+ int left = 0;
+ int plus = 0;
+ int space = 0;
+ int alt = 0;
+ int zero = 0;
+ int width = -1;
+ int precision = -1;
+ int lcount = 0;
+ char spec;
+
+ while (*fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '#' || *fmt == '0') {
+ if (*fmt == '-') left = 1;
+ else if (*fmt == '+') plus = 1;
+ else if (*fmt == ' ') space = 1;
+ else if (*fmt == '#') alt = 1;
+ else if (*fmt == '0') zero = 1;
+ fmt++;
+ }
+
+ if (*fmt == '*') {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ left = 1;
+ width = -width;
+ }
+ fmt++;
+ } else if (isdigit((unsigned char)*fmt)) {
+ width = 0;
+ while (isdigit((unsigned char)*fmt)) {
+ width = width * 10 + (*fmt - '0');
+ fmt++;
+ }
+ }
+
+ if (*fmt == '.') {
+ fmt++;
+ precision = 0;
+ if (*fmt == '*') {
+ precision = va_arg(ap, int);
+ if (precision < 0) {
+ precision = -1;
+ }
+ fmt++;
+ } else {
+ while (isdigit((unsigned char)*fmt)) {
+ precision = precision * 10 + (*fmt - '0');
+ fmt++;
+ }
+ }
+ }
+
+ while (*fmt == 'l') {
+ lcount++;
+ fmt++;
+ }
+
+ spec = *fmt;
+ if (!spec) {
+ break;
+ }
+
+ switch (spec) {
+ case 'd':
+ case 'i': {
+ long long sv;
+ unsigned long long uv;
+ char digits[64];
+ size_t dlen = 0;
+ int neg = 0;
+ char signch = '\0';
+ int zeros = 0;
+ int spaces = 0;
+ int total;
+
+ if (lcount >= 2) sv = va_arg(ap, long long);
+ else if (lcount == 1) sv = va_arg(ap, long);
+ else sv = va_arg(ap, int);
+
+ if (sv < 0) {
+ neg = 1;
+ uv = (unsigned long long)(-(sv + 1)) + 1ULL;
+ } else {
+ uv = (unsigned long long)sv;
+ }
+
+ if (neg) signch = '-';
+ else if (plus) signch = '+';
+ else if (space) signch = ' ';
+
+ if (!(precision == 0 && uv == 0ULL)) {
+ _b_utoa(uv, 10U, 0, digits, &dlen);
+ }
+
+ if (precision > 0 && (size_t)precision > dlen) {
+ zeros = precision - (int)dlen;
+ }
+
+ if (precision < 0 && zero && !left && width > 0) {
+ int signw = (signch != '\0') ? 1 : 0;
+ int need = width - (signw + (int)dlen);
+ if (need > zeros) {
+ zeros = need;
+ }
+ }
+
+ total = ((signch != '\0') ? 1 : 0) + zeros + (int)dlen;
+ if (width > total) {
+ spaces = width - total;
+ }
+
+ if (!left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ if (signch != '\0') {
+ _b_append_char(str, size, &out_i, signch);
+ }
+ _b_append_repeat(str, size, &out_i, '0', zeros);
+ _b_append_strn(str, size, &out_i, digits, dlen);
+ if (left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ break;
+ }
+
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'o': {
+ unsigned long long uv;
+ unsigned base = (spec == 'o') ? 8U : ((spec == 'u') ? 10U : 16U);
+ int upper = (spec == 'X');
+ char digits[64];
+ size_t dlen = 0;
+ int zeros = 0;
+ int spaces = 0;
+ int prefix_len = 0;
+ char p1 = '\0';
+ char p2 = '\0';
+ int total;
+
+ if (lcount >= 2) uv = va_arg(ap, unsigned long long);
+ else if (lcount == 1) uv = va_arg(ap, unsigned long);
+ else uv = va_arg(ap, unsigned int);
+
+ if (!(precision == 0 && uv == 0ULL)) {
+ _b_utoa(uv, base, upper, digits, &dlen);
+ }
+
+ if (alt && uv != 0ULL) {
+ if (spec == 'x' || spec == 'X') {
+ p1 = '0';
+ p2 = upper ? 'X' : 'x';
+ prefix_len = 2;
+ } else if (spec == 'o') {
+ p1 = '0';
+ prefix_len = 1;
+ }
+ }
+
+ if (precision > 0 && (size_t)precision > dlen) {
+ zeros = precision - (int)dlen;
+ }
+
+ if (precision < 0 && zero && !left && width > 0) {
+ int need = width - (prefix_len + (int)dlen);
+ if (need > zeros) {
+ zeros = need;
+ }
+ }
+
+ total = prefix_len + zeros + (int)dlen;
+ if (width > total) {
+ spaces = width - total;
+ }
+
+ if (!left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ if (prefix_len >= 1) {
+ _b_append_char(str, size, &out_i, p1);
+ }
+ if (prefix_len >= 2) {
+ _b_append_char(str, size, &out_i, p2);
+ }
+ _b_append_repeat(str, size, &out_i, '0', zeros);
+ _b_append_strn(str, size, &out_i, digits, dlen);
+ if (left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ break;
+ }
+
+ case 'c': {
+ int c = va_arg(ap, int);
+ int spaces = (width > 1) ? (width - 1) : 0;
+ if (!left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ _b_append_char(str, size, &out_i, c);
+ if (left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ break;
+ }
+
+ case 's': {
+ const char *s = va_arg(ap, const char *);
+ size_t slen;
+ int spaces;
+ if (!s) s = "(null)";
+ slen = strlen(s);
+ if (precision >= 0 && (size_t)precision < slen) {
+ slen = (size_t)precision;
+ }
+ spaces = (width > (int)slen) ? (width - (int)slen) : 0;
+ if (!left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ _b_append_strn(str, size, &out_i, s, slen);
+ if (left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ break;
+ }
+
+ case 'p': {
+ uintptr_t v = (uintptr_t)va_arg(ap, void *);
+ char nbuf[32];
+ size_t nlen = 0;
+ _b_append_strn(str, size, &out_i, "0x", 2);
+ _b_utoa((unsigned long long)v, 16U, 0, nbuf, &nlen);
+ _b_append_strn(str, size, &out_i, nbuf, nlen);
+ break;
+ }
+
+ case 'f':
+ case 'g':
+ case 'e': {
+ double v = va_arg(ap, double);
+ char nbuf[96];
+ size_t nlen = 0;
+ int spaces;
+ _b_ftoa(v, precision, nbuf, &nlen);
+ spaces = (width > (int)nlen) ? (width - (int)nlen) : 0;
+ if (!left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ _b_append_strn(str, size, &out_i, nbuf, nlen);
+ if (left) {
+ _b_append_repeat(str, size, &out_i, ' ', spaces);
+ }
+ break;
+ }
+
+ default:
+ _b_append_char(str, size, &out_i, '%');
+ _b_append_char(str, size, &out_i, spec);
+ break;
+ }
+ }
+
+ if (*fmt) {
+ fmt++;
+ }
+ }
+
+ if (size > 0) {
+ size_t term = (out_i < size - 1) ? out_i : (size - 1);
+ str[term] = '\0';
+ }
+
+ return (int)out_i;
+}
+
+__attribute__((weak)) int snprintf(char *str, size_t size, const char *fmt, ...) {
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = vsnprintf(str, size, fmt, ap);
+ va_end(ap);
+ return n;
+}
+
+__attribute__((weak)) int sprintf(char *str, const char *fmt, ...) {
+ int n;
+ va_list ap;
+ va_start(ap, fmt);
+ n = vsnprintf(str, (size_t)-1, fmt, ap);
+ va_end(ap);
+ return n;
+}
+
+__attribute__((weak)) int fprintf(FILE *stream, const char *fmt, ...) {
+ char buf[1024];
+ int len;
+ va_list ap;
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (len <= 0) {
+ return len;
+ }
+ if ((size_t)len > sizeof(buf)) {
+ len = (int)sizeof(buf);
+ }
+ if (fwrite(buf, 1, (size_t)len, stream) == 0) {
+ return -1;
+ }
+ return len;
+}
+
+__attribute__((weak)) int vfprintf(FILE *stream, const char *fmt, va_list ap) {
+ char buf[1024];
+ int len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ if (len <= 0) {
+ return len;
+ }
+ if ((size_t)len > sizeof(buf)) {
+ len = (int)sizeof(buf);
+ }
+ if (fwrite(buf, 1, (size_t)len, stream) == 0) {
+ return -1;
+ }
+ return len;
+}
+
+__attribute__((weak)) int fputc(int c, FILE *stream) {
+ unsigned char ch = (unsigned char)c;
+ if (!stream) {
+ return EOF;
+ }
+ if (fwrite(&ch, 1, 1, stream) != 1) {
+ return EOF;
+ }
+ return c;
+}
+
+__attribute__((weak)) int putchar(int c) {
+ return fputc(c, stdout);
+}
+
+__attribute__((weak)) long filelength(FILE *f) {
+ if (!f) {
+ return -1;
+ }
+ return (long)sys_size(f->fd);
+}
+
+static const char *_b_skip_spaces(const char *p) {
+ while (*p && _b_is_space_char((unsigned char)*p)) {
+ p++;
+ }
+ return p;
+}
+
+static int _b_digit_val(int c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
+ if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
+ return -1;
+}
+
+static int _b_parse_u64(const char **sp, int base, int width, unsigned long long *out) {
+ const char *p = *sp;
+ unsigned long long v = 0;
+ int any = 0;
+
+ if (width != 0 && base == 16 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ p += 2;
+ if (width > 0) {
+ width -= 2;
+ }
+ }
+
+ while (*p && (width < 0 || width > 0)) {
+ int d = _b_digit_val((unsigned char)*p);
+ if (d < 0 || d >= base) {
+ break;
+ }
+ v = v * (unsigned long long)base + (unsigned long long)d;
+ any = 1;
+ p++;
+ if (width > 0) {
+ width--;
+ }
+ }
+
+ if (!any) {
+ return 0;
+ }
+ *sp = p;
+ *out = v;
+ return 1;
+}
+
+static int _b_parse_auto_int(const char **sp, int width, long long *out) {
+ const char *p = *sp;
+ int neg = 0;
+ int base = 10;
+ unsigned long long uv;
+
+ if (*p == '+' || *p == '-') {
+ neg = (*p == '-');
+ p++;
+ if (width > 0) {
+ width--;
+ }
+ }
+
+ if ((width < 0 || width >= 2) && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ base = 16;
+ } else if ((width < 0 || width >= 1) && p[0] == '0') {
+ base = 8;
+ }
+
+ if (!_b_parse_u64(&p, base, width, &uv)) {
+ return 0;
+ }
+ *sp = p;
+ *out = neg ? -(long long)uv : (long long)uv;
+ return 1;
+}
+
+static int _b_parse_s64(const char **sp, int base, int width, long long *out) {
+ const char *p = *sp;
+ int neg = 0;
+ unsigned long long uv;
+
+ if (*p == '+' || *p == '-') {
+ neg = (*p == '-');
+ p++;
+ if (width > 0) {
+ width--;
+ }
+ }
+ if (!_b_parse_u64(&p, base, width, &uv)) {
+ return 0;
+ }
+ *sp = p;
+ *out = neg ? -(long long)uv : (long long)uv;
+ return 1;
+}
+
+static int _b_vsscanf_core(const char *str, const char *fmt, va_list ap) {
+ const char *s = str;
+ const char *f = fmt;
+ int assigned = 0;
+
+ while (*f) {
+ if (_b_is_space_char((unsigned char)*f)) {
+ while (_b_is_space_char((unsigned char)*f)) f++;
+ s = _b_skip_spaces(s);
+ continue;
+ }
+
+ if (*f != '%') {
+ if (*s != *f) {
+ break;
+ }
+ s++;
+ f++;
+ continue;
+ }
+
+ f++;
+ if (*f == '%') {
+ if (*s != '%') {
+ break;
+ }
+ s++;
+ f++;
+ continue;
+ }
+
+ {
+ int suppress = 0;
+ int width = -1;
+ int lcount = 0;
+
+ if (*f == '*') {
+ suppress = 1;
+ f++;
+ }
+
+ if (isdigit((unsigned char)*f)) {
+ width = 0;
+ while (isdigit((unsigned char)*f)) {
+ width = width * 10 + (*f - '0');
+ f++;
+ }
+ }
+
+ while (*f == 'l') {
+ lcount++;
+ f++;
+ }
+
+ if (*f != 'c' && *f != 'n') {
+ s = _b_skip_spaces(s);
+ }
+
+ switch (*f) {
+ case 'd': {
+ long long v;
+ if (!_b_parse_s64(&s, 10, width, &v)) return assigned;
+ if (!suppress) {
+ if (lcount >= 1) *va_arg(ap, long *) = (long)v;
+ else *va_arg(ap, int *) = (int)v;
+ assigned++;
+ }
+ break;
+ }
+ case 'i': {
+ long long v;
+ if (!_b_parse_auto_int(&s, width, &v)) return assigned;
+ if (!suppress) {
+ if (lcount >= 1) *va_arg(ap, long *) = (long)v;
+ else *va_arg(ap, int *) = (int)v;
+ assigned++;
+ }
+ break;
+ }
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'o': {
+ int base = (*f == 'o') ? 8 : ((*f == 'u') ? 10 : 16);
+ unsigned long long v;
+ if (!_b_parse_u64(&s, base, width, &v)) return assigned;
+ if (!suppress) {
+ if (lcount >= 1) *va_arg(ap, unsigned long *) = (unsigned long)v;
+ else *va_arg(ap, unsigned *) = (unsigned)v;
+ assigned++;
+ }
+ break;
+ }
+ case 'c': {
+ int count = (width > 0) ? width : 1;
+ if (!*s) return assigned;
+ if (!suppress) {
+ char *dst = va_arg(ap, char *);
+ while (count-- > 0 && *s) {
+ *dst++ = *s++;
+ }
+ assigned++;
+ } else {
+ while (count-- > 0 && *s) s++;
+ }
+ break;
+ }
+ case 's': {
+ int wrote = 0;
+ if (!*s) return assigned;
+ if (!suppress) {
+ char *dst = va_arg(ap, char *);
+ while (*s && !_b_is_space_char((unsigned char)*s) && (width < 0 || width-- > 0)) {
+ *dst++ = *s++;
+ wrote = 1;
+ }
+ if (!wrote) return assigned;
+ *dst = '\0';
+ assigned++;
+ } else {
+ while (*s && !_b_is_space_char((unsigned char)*s) && (width < 0 || width-- > 0)) {
+ s++;
+ wrote = 1;
+ }
+ if (!wrote) return assigned;
+ }
+ break;
+ }
+ case 'n': {
+ if (!suppress) {
+ *va_arg(ap, int *) = (int)(s - str);
+ }
+ break;
+ }
+ default:
+ return assigned;
+ }
+ }
+
+ if (*f) {
+ f++;
+ }
+ }
+
+ return assigned;
+}
+
+__attribute__((weak)) int sscanf(const char *str, const char *fmt, ...) {
+ int n;
+ va_list ap;
+ if (!str || !fmt) {
+ errno = EINVAL;
+ return 0;
+ }
+ va_start(ap, fmt);
+ n = _b_vsscanf_core(str, fmt, ap);
+ va_end(ap);
+ return n;
+}
diff --git a/src/userland/libc/stdio.h b/src/userland/libc/stdio.h
new file mode 100644
index 0000000..4d8447f
--- /dev/null
+++ b/src/userland/libc/stdio.h
@@ -0,0 +1,56 @@
+#ifndef BOREDOS_LIBC_STDIO_H
+#define BOREDOS_LIBC_STDIO_H
+
+#include
+#include
+
+typedef struct BOREDOS_FILE {
+ int fd;
+ int eof;
+ int err;
+ int has_ungetc;
+ int ungetc_char;
+} FILE;
+
+extern FILE *stdin;
+extern FILE *stdout;
+extern FILE *stderr;
+
+#define EOF (-1)
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#define BUFSIZ 1024
+#define FILENAME_MAX 260
+#define TMP_MAX 32
+
+FILE *fopen(const char *path, const char *mode);
+FILE *freopen(const char *path, const char *mode, FILE *stream);
+int fclose(FILE *stream);
+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);
+int fseek(FILE *stream, long offset, int whence);
+long ftell(FILE *stream);
+int getc(FILE *stream);
+int ungetc(int c, FILE *stream);
+char *fgets(char *s, int n, FILE *stream);
+int fputs(const char *s, FILE *stream);
+int feof(FILE *stream);
+int ferror(FILE *stream);
+void clearerr(FILE *stream);
+int fflush(FILE *stream);
+int fputc(int c, FILE *stream);
+int putchar(int c);
+int fprintf(FILE *stream, const char *fmt, ...);
+int vfprintf(FILE *stream, const char *fmt, va_list ap);
+long filelength(FILE *f);
+int vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
+int snprintf(char *str, size_t size, const char *fmt, ...);
+int sprintf(char *str, const char *fmt, ...);
+int sscanf(const char *str, const char *fmt, ...);
+int remove(const char *path);
+int rename(const char *oldpath, const char *newpath);
+FILE *tmpfile(void);
+char *tmpnam(char *s);
+
+#endif
diff --git a/src/userland/libc/stdlib.c b/src/userland/libc/stdlib.c
index 2df15fb..a471ec4 100644
--- a/src/userland/libc/stdlib.c
+++ b/src/userland/libc/stdlib.c
@@ -334,7 +334,7 @@ char* getcwd(char *buf, int size) {
}
void sleep(int ms) {
- sys_system(46, ms, 0, 0, 0);
+ sys_system(SYSTEM_CMD_SLEEP, ms, 0, 0, 0);
}
void exit(int status) {
diff --git a/src/userland/libc/stdlib.h b/src/userland/libc/stdlib.h
index 2b4818d..8e87f46 100644
--- a/src/userland/libc/stdlib.h
+++ b/src/userland/libc/stdlib.h
@@ -16,15 +16,24 @@ void *memcpy(void *dest, const void *src, size_t n);
// Math/Utility functions
int atoi(const char *nptr);
void itoa(int n, char *buf);
+int abs(int x);
+double strtod(const char *nptr, char **endptr);
// IO functions
void puts(const char *s);
void printf(const char *fmt, ...);
+// Runtime stubs
+int system(const char *command);
+char *getenv(const char *name);
+void abort(void);
+
// System/Process functions
int chdir(const char *path);
char* getcwd(char *buf, int size);
+int access(const char *pathname, int mode);
void sleep(int ms);
void exit(int status);
+void _exit(int status);
#endif
diff --git a/src/userland/libc/string.h b/src/userland/libc/string.h
index a0e32ec..a5eab52 100644
--- a/src/userland/libc/string.h
+++ b/src/userland/libc/string.h
@@ -7,11 +7,22 @@ void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
+void *memchr(const void *s, int c, size_t n);
char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+char *strpbrk(const char *s, const char *accept);
char *strstr(const char *haystack, const char *needle);
+size_t strspn(const char *s, const char *accept);
+size_t strcspn(const char *s, const char *reject);
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t n);
+int strncasecmp(const char *s1, const char *s2, size_t n);
+int strcasecmp(const char *s1, const char *s2);
+int strcoll(const char *s1, const char *s2);
char* strcpy(char *dest, const char *src);
char* strcat(char *dest, const char *src);
+char *strdup(const char *s);
+char *strerror(int errnum);
#endif
diff --git a/src/userland/libc/string_ext.c b/src/userland/libc/string_ext.c
new file mode 100644
index 0000000..06abb5e
--- /dev/null
+++ b/src/userland/libc/string_ext.c
@@ -0,0 +1,158 @@
+#include "string.h"
+#include "errno.h"
+#include "ctype.h"
+#include "stdlib.h"
+
+__attribute__((weak)) int strncmp(const char *s1, const char *s2, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ unsigned char c1 = (unsigned char)s1[i];
+ unsigned char c2 = (unsigned char)s2[i];
+ if (c1 != c2) {
+ return (int)c1 - (int)c2;
+ }
+ if (c1 == '\0') {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+__attribute__((weak)) char *strncpy(char *dest, const char *src, size_t n) {
+ size_t i;
+ for (i = 0; i < n && src[i] != '\0'; i++) {
+ dest[i] = src[i];
+ }
+ for (; i < n; i++) {
+ dest[i] = '\0';
+ }
+ return dest;
+}
+
+__attribute__((weak)) char *strncat(char *dest, const char *src, size_t n) {
+ size_t dlen = strlen(dest);
+ size_t i;
+ for (i = 0; i < n && src[i] != '\0'; i++) {
+ dest[dlen + i] = src[i];
+ }
+ dest[dlen + i] = '\0';
+ return dest;
+}
+
+__attribute__((weak)) char *strrchr(const char *s, int c) {
+ const char *last = NULL;
+ for (;; s++) {
+ if (*s == (char)c) {
+ last = s;
+ }
+ if (*s == '\0') {
+ break;
+ }
+ }
+ return (char *)last;
+}
+
+__attribute__((weak)) char *strpbrk(const char *s, const char *accept) {
+ for (; *s; s++) {
+ const char *a;
+ for (a = accept; *a; a++) {
+ if (*s == *a) {
+ return (char *)s;
+ }
+ }
+ }
+ return NULL;
+}
+
+__attribute__((weak)) size_t strspn(const char *s, const char *accept) {
+ size_t n = 0;
+ while (*s) {
+ if (!strchr(accept, *s)) {
+ break;
+ }
+ n++;
+ s++;
+ }
+ return n;
+}
+
+__attribute__((weak)) size_t strcspn(const char *s, const char *reject) {
+ size_t n = 0;
+ while (*s) {
+ if (strchr(reject, *s)) {
+ break;
+ }
+ n++;
+ s++;
+ }
+ return n;
+}
+
+__attribute__((weak)) void *memchr(const void *s, int c, size_t n) {
+ const unsigned char *p = (const unsigned char *)s;
+ size_t i;
+ for (i = 0; i < n; i++) {
+ if (p[i] == (unsigned char)c) {
+ return (void *)(p + i);
+ }
+ }
+ return NULL;
+}
+
+__attribute__((weak)) int strcoll(const char *s1, const char *s2) {
+ return strcmp(s1, s2);
+}
+
+__attribute__((weak)) char *strerror(int errnum) {
+ switch (errnum) {
+ case 0: return "no error";
+ case EDOM: return "domain error";
+ case ERANGE: return "range error";
+ case EINVAL: return "invalid argument";
+ case ENOENT: return "no such file or directory";
+ case ENOMEM: return "not enough memory";
+ case EEXIST: return "file exists";
+ case EIO: return "i/o error";
+ case ENOSYS: return "function not implemented";
+ default: return "unknown error";
+ }
+}
+
+__attribute__((weak)) int strncasecmp(const char *s1, const char *s2, size_t n) {
+ while (n--) {
+ char c1 = (char)tolower((unsigned char)*s1++);
+ char c2 = (char)tolower((unsigned char)*s2++);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ if (!c1) {
+ break;
+ }
+ }
+ return 0;
+}
+
+__attribute__((weak)) int strcasecmp(const char *s1, const char *s2) {
+ while (1) {
+ char c1 = (char)tolower((unsigned char)*s1++);
+ char c2 = (char)tolower((unsigned char)*s2++);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ if (!c1) {
+ break;
+ }
+ }
+ return 0;
+}
+
+__attribute__((weak)) char *strdup(const char *s) {
+ size_t len = strlen(s) + 1;
+ char *dup = (char *)malloc(len);
+ if (!dup) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memcpy(dup, s, len);
+ return dup;
+}
diff --git a/src/userland/libc/sys/stat.h b/src/userland/libc/sys/stat.h
new file mode 100644
index 0000000..e3cfdb8
--- /dev/null
+++ b/src/userland/libc/sys/stat.h
@@ -0,0 +1,47 @@
+#ifndef BOREDOS_LIBC_SYS_STAT_H
+#define BOREDOS_LIBC_SYS_STAT_H
+
+#include "types.h"
+
+typedef long time_t;
+
+struct stat {
+ unsigned long st_dev;
+ unsigned long st_ino;
+ mode_t st_mode;
+ unsigned long st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ unsigned long st_rdev;
+ int st_size;
+ long st_blksize;
+ long st_blocks;
+ time_t st_atime;
+ time_t st_mtime;
+ time_t st_ctime;
+};
+
+#define S_IFMT 0170000
+#define S_IFIFO 0010000
+#define S_IFCHR 0020000
+#define S_IFDIR 0040000
+#define S_IFREG 0100000
+
+#define S_IRUSR 0400
+#define S_IWUSR 0200
+#define S_IXUSR 0100
+#define S_IRGRP 0040
+#define S_IWGRP 0020
+#define S_IXGRP 0010
+#define S_IROTH 0004
+#define S_IWOTH 0002
+#define S_IXOTH 0001
+
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+
+int stat(const char *pathname, struct stat *statbuf);
+int fstat(int fd, struct stat *statbuf);
+int mkdir(const char *pathname, int mode);
+
+#endif
diff --git a/src/userland/libc/sys/types.h b/src/userland/libc/sys/types.h
new file mode 100644
index 0000000..da5815b
--- /dev/null
+++ b/src/userland/libc/sys/types.h
@@ -0,0 +1,11 @@
+#ifndef BOREDOS_LIBC_SYS_TYPES_H
+#define BOREDOS_LIBC_SYS_TYPES_H
+
+typedef long ssize_t;
+typedef long off_t;
+typedef unsigned int mode_t;
+typedef int pid_t;
+typedef unsigned int uid_t;
+typedef unsigned int gid_t;
+
+#endif
diff --git a/src/userland/libc/sys/wait.h b/src/userland/libc/sys/wait.h
new file mode 100644
index 0000000..6e0133c
--- /dev/null
+++ b/src/userland/libc/sys/wait.h
@@ -0,0 +1,15 @@
+#ifndef BOREDOS_LIBC_SYS_WAIT_H
+#define BOREDOS_LIBC_SYS_WAIT_H
+
+#include "../sys/types.h"
+
+#define WNOHANG 1
+
+#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
+#define WIFEXITED(status) ((((status) & 0x7f) == 0) ? 1 : 0)
+#define WTERMSIG(status) ((status) & 0x7f)
+#define WIFSIGNALED(status) (((status) & 0x7f) != 0)
+
+pid_t waitpid(pid_t pid, int *status, int options);
+
+#endif
diff --git a/src/userland/libc/syscall.c b/src/userland/libc/syscall.c
index 5e433c3..f5068c4 100644
--- a/src/userland/libc/syscall.c
+++ b/src/userland/libc/syscall.c
@@ -145,6 +145,22 @@ int sys_chdir(const char *path) {
return (int)syscall2(SYS_FS, FS_CMD_CHDIR, (uint64_t)path);
}
+int sys_dup(int oldfd) {
+ return (int)syscall2(SYS_FS, FS_CMD_DUP, (uint64_t)oldfd);
+}
+
+int sys_dup2(int oldfd, int newfd) {
+ return (int)syscall3(SYS_FS, FS_CMD_DUP2, (uint64_t)oldfd, (uint64_t)newfd);
+}
+
+int sys_pipe(int pipefd[2]) {
+ return (int)syscall2(SYS_FS, FS_CMD_PIPE, (uint64_t)pipefd);
+}
+
+int sys_fcntl(int fd, int cmd, int val) {
+ return (int)syscall4(SYS_FS, FS_CMD_FCNTL, (uint64_t)fd, (uint64_t)cmd, (uint64_t)val);
+}
+
int sys_tty_create(void) {
return (int)syscall2(SYS_SYSTEM, SYSTEM_CMD_TTY_CREATE, 0);
}
@@ -165,6 +181,30 @@ int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_i
return (int)syscall5(SYS_SYSTEM, SYSTEM_CMD_SPAWN, (uint64_t)path, (uint64_t)args, flags, (uint64_t)tty_id);
}
+int sys_exec(const char *path, const char *args) {
+ return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_EXEC, (uint64_t)path, (uint64_t)args, 0);
+}
+
+int sys_waitpid(int pid, int *status, int options) {
+ return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_WAITPID, (uint64_t)pid, (uint64_t)status, (uint64_t)options);
+}
+
+int sys_kill_signal(int pid, int sig) {
+ return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_KILL_SIGNAL, (uint64_t)pid, (uint64_t)sig, 0);
+}
+
+int sys_sigaction(int sig, const void *act, void *oldact) {
+ return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_SIGACTION, (uint64_t)sig, (uint64_t)act, (uint64_t)oldact);
+}
+
+int sys_sigprocmask(int how, const unsigned long *set, unsigned long *oldset) {
+ return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_SIGPROCMASK, (uint64_t)how, (uint64_t)set, (uint64_t)oldset);
+}
+
+int sys_sigpending(unsigned long *set) {
+ return (int)syscall3(SYS_SYSTEM, SYSTEM_CMD_SIGPENDING, (uint64_t)set, 0);
+}
+
int sys_tty_set_fg(int tty_id, int pid) {
return (int)syscall4(SYS_SYSTEM, SYSTEM_CMD_TTY_SET_FG, (uint64_t)tty_id, (uint64_t)pid, 0);
}
diff --git a/src/userland/libc/syscall.h b/src/userland/libc/syscall.h
index f6f168a..6b03bd9 100644
--- a/src/userland/libc/syscall.h
+++ b/src/userland/libc/syscall.h
@@ -28,6 +28,10 @@
#define FS_CMD_GETCWD 12
#define FS_CMD_CHDIR 13
#define FS_CMD_GET_INFO 14
+#define FS_CMD_DUP 15
+#define FS_CMD_DUP2 16
+#define FS_CMD_PIPE 17
+#define FS_CMD_FCNTL 18
// System Commands (via SYS_SYSTEM)
#define SYSTEM_CMD_SET_BG_COLOR 1
@@ -44,6 +48,8 @@
#define SYSTEM_CMD_REBOOT 12
#define SYSTEM_CMD_SHUTDOWN 13
#define SYSTEM_CMD_BEEP 14
+#define SYSTEM_CMD_GET_MEM_INFO 15
+#define SYSTEM_CMD_GET_TICKS 16
#define SYSTEM_CMD_PCI_LIST 17
#define SYSTEM_CMD_NETWORK_DHCP 18
#define SYSTEM_CMD_NETWORK_GET_MAC 19
@@ -60,6 +66,7 @@
#define SYSTEM_CMD_NETWORK_GET_NIC_NAME 48
#define SYSTEM_CMD_SET_TEXT_COLOR 29
#define SYSTEM_CMD_SET_WALLPAPER_PATH 31
+#define SYSTEM_CMD_RTC_SET 32
#define SYSTEM_CMD_TCP_CONNECT 33
#define SYSTEM_CMD_TCP_SEND 34
#define SYSTEM_CMD_TCP_RECV 35
@@ -67,10 +74,12 @@
#define SYSTEM_CMD_DNS_LOOKUP 37
#define SYSTEM_CMD_SET_DNS 38
#define SYSTEM_CMD_NET_UNLOCK 39
+#define SYSTEM_CMD_SET_FONT 40
#define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43
+#define SYSTEM_CMD_SET_RESOLUTION 47
#define SYSTEM_CMD_PARALLEL_RUN 50
#define SYSTEM_CMD_TTY_CREATE 60
#define SYSTEM_CMD_TTY_READ_OUT 61
@@ -82,10 +91,17 @@
#define SYSTEM_CMD_TTY_KILL_FG 67
#define SYSTEM_CMD_TTY_KILL_ALL 68
#define SYSTEM_CMD_TTY_DESTROY 69
+#define SYSTEM_CMD_EXEC 70
+#define SYSTEM_CMD_WAITPID 71
+#define SYSTEM_CMD_KILL_SIGNAL 72
+#define SYSTEM_CMD_SIGACTION 73
+#define SYSTEM_CMD_SIGPROCMASK 74
+#define SYSTEM_CMD_SIGPENDING 75
#define SPAWN_FLAG_TERMINAL 0x1
#define SPAWN_FLAG_INHERIT_TTY 0x2
#define SPAWN_FLAG_TTY_ID 0x4
+#define SPAWN_FLAG_BACKGROUND 0x8
// Internal assembly entry into Ring 0
extern uint64_t syscall0(uint64_t sys_num);
@@ -128,12 +144,22 @@ int sys_mkdir(const char *path);
int sys_exists(const char *path);
int sys_getcwd(char *buf, int size);
int sys_chdir(const char *path);
+int sys_dup(int oldfd);
+int sys_dup2(int oldfd, int newfd);
+int sys_pipe(int pipefd[2]);
+int sys_fcntl(int fd, int cmd, int val);
int sys_tty_create(void);
int sys_tty_read_out(int tty_id, char *buf, int len);
int sys_tty_write_in(int tty_id, const char *buf, int len);
int sys_tty_read_in(char *buf, int len);
int sys_spawn(const char *path, const char *args, uint64_t flags, uint64_t tty_id);
+int sys_exec(const char *path, const char *args);
+int sys_waitpid(int pid, int *status, int options);
+int sys_kill_signal(int pid, int sig);
+int sys_sigaction(int sig, const void *act, void *oldact);
+int sys_sigprocmask(int how, const unsigned long *set, unsigned long *oldset);
+int sys_sigpending(unsigned long *set);
int sys_tty_set_fg(int tty_id, int pid);
int sys_tty_get_fg(int tty_id);
int sys_tty_kill_fg(int tty_id);
diff --git a/src/userland/libc/time.c b/src/userland/libc/time.c
new file mode 100644
index 0000000..6bb1248
--- /dev/null
+++ b/src/userland/libc/time.c
@@ -0,0 +1,166 @@
+#include
+
+#include "time.h"
+#include "stdio.h"
+#include "syscall.h"
+
+static int _b_is_leap(int year) {
+ return ((year % 4) == 0 && (year % 100) != 0) || ((year % 400) == 0);
+}
+
+static int _b_days_in_month(int year, int month) {
+ static const int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ if (month == 1 && _b_is_leap(year)) {
+ return 29;
+ }
+ return mdays[month];
+}
+
+static long long _b_days_before_year(int year) {
+ long long y = (long long)year - 1;
+ return y * 365 + y / 4 - y / 100 + y / 400;
+}
+
+static long long _b_days_since_epoch(int year, int month, int day) {
+ long long days = _b_days_before_year(year) - _b_days_before_year(1970);
+ int m;
+ for (m = 0; m < month - 1; m++) {
+ days += _b_days_in_month(year, m);
+ }
+ days += (day - 1);
+ return days;
+}
+
+static void _b_civil_from_days(long long z, int *year, int *month, int *day) {
+ z += 719468;
+ long long era = (z >= 0 ? z : z - 146096) / 146097;
+ unsigned doe = (unsigned)(z - era * 146097);
+ unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
+ int y = (int)yoe + (int)era * 400;
+ unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
+ unsigned mp = (5 * doy + 2) / 153;
+ unsigned d = doy - (153 * mp + 2) / 5 + 1;
+ unsigned m = mp + (mp < 10 ? 3 : (unsigned)-9);
+ y += (m <= 2);
+ *year = y;
+ *month = (int)m;
+ *day = (int)d;
+}
+
+static time_t _b_seconds_from_ymdhms(int year, int month, int day, int hour, int minute, int second) {
+ long long days = _b_days_since_epoch(year, month, day);
+ return (time_t)(days * 86400LL + hour * 3600LL + minute * 60LL + second);
+}
+
+static void _b_fill_tm_from_epoch(time_t t, struct tm *out) {
+ long long sec = (long long)t;
+ long long days;
+ int sod;
+ int year;
+ int month;
+ int day;
+
+ if (sec < 0) {
+ long long d = ((-sec) + 86399LL) / 86400LL;
+ sec += d * 86400LL;
+ }
+
+ days = sec / 86400LL;
+ sod = (int)(sec % 86400LL);
+ if (sod < 0) {
+ sod += 86400;
+ days--;
+ }
+
+ _b_civil_from_days(days, &year, &month, &day);
+
+ out->tm_year = year - 1900;
+ out->tm_mon = month - 1;
+ out->tm_mday = day;
+ out->tm_hour = sod / 3600;
+ out->tm_min = (sod % 3600) / 60;
+ out->tm_sec = sod % 60;
+ out->tm_wday = (int)((days + 4) % 7);
+ if (out->tm_wday < 0) out->tm_wday += 7;
+
+ {
+ long long jan1 = _b_days_since_epoch(year, 1, 1);
+ out->tm_yday = (int)(days - jan1);
+ }
+ out->tm_isdst = 0;
+}
+
+__attribute__((weak)) time_t time(time_t *out) {
+ int dt[6] = {1970, 1, 1, 0, 0, 0};
+ time_t t;
+ if (sys_system(SYSTEM_CMD_RTC_GET, 0, (uint64_t)dt, 0, 0) != 0) {
+ t = 0;
+ } else {
+ t = _b_seconds_from_ymdhms(dt[0], dt[1], dt[2], dt[3], dt[4], dt[5]);
+ }
+ if (out) {
+ *out = t;
+ }
+ return t;
+}
+
+__attribute__((weak)) clock_t clock(void) {
+ static uint64_t start_tsc = 0;
+ unsigned int lo;
+ unsigned int hi;
+ uint64_t now_tsc;
+
+ __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
+ now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo;
+
+ if (start_tsc == 0) {
+ start_tsc = now_tsc;
+ __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
+ now_tsc = ((uint64_t)hi << 32) | (uint64_t)lo;
+ }
+
+ return (clock_t)(now_tsc - start_tsc);
+}
+
+__attribute__((weak)) struct tm *gmtime(const time_t *timer) {
+ static struct tm tmv;
+ if (!timer) {
+ return NULL;
+ }
+ _b_fill_tm_from_epoch(*timer, &tmv);
+ return &tmv;
+}
+
+__attribute__((weak)) struct tm *localtime(const time_t *timer) {
+ return gmtime(timer);
+}
+
+__attribute__((weak)) size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm) {
+ (void)fmt;
+ if (!s || max == 0 || !tm) {
+ return 0;
+ }
+ {
+ int n = snprintf(s, max, "%04d-%02d-%02d %02d:%02d:%02d",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ if (n < 0 || (size_t)n >= max) {
+ if (max > 0) s[0] = '\0';
+ return 0;
+ }
+ return (size_t)n;
+ }
+}
+
+__attribute__((weak)) time_t mktime(struct tm *tm) {
+ if (!tm) {
+ return (time_t)-1;
+ }
+ return _b_seconds_from_ymdhms(
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+}
diff --git a/src/userland/libc/time.h b/src/userland/libc/time.h
new file mode 100644
index 0000000..b3e7c3c
--- /dev/null
+++ b/src/userland/libc/time.h
@@ -0,0 +1,30 @@
+#ifndef BOREDOS_LIBC_TIME_H
+#define BOREDOS_LIBC_TIME_H
+
+#include
+
+typedef long long time_t;
+typedef unsigned long long clock_t;
+
+#define CLOCKS_PER_SEC 3000000000ULL
+
+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;
+};
+
+time_t time(time_t *out);
+clock_t clock(void);
+struct tm *localtime(const time_t *timer);
+struct tm *gmtime(const time_t *timer);
+size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm);
+time_t mktime(struct tm *tm);
+
+#endif
diff --git a/src/userland/libc/unistd.h b/src/userland/libc/unistd.h
new file mode 100644
index 0000000..36d7081
--- /dev/null
+++ b/src/userland/libc/unistd.h
@@ -0,0 +1,30 @@
+#ifndef BOREDOS_LIBC_UNISTD_H
+#define BOREDOS_LIBC_UNISTD_H
+
+#include
+#include "sys/types.h"
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+#define F_OK 0
+#define X_OK 1
+#define W_OK 2
+#define R_OK 4
+
+int close(int fd);
+ssize_t read(int fd, void *buf, size_t count);
+ssize_t write(int fd, const void *buf, size_t count);
+off_t lseek(int fd, off_t offset, int whence);
+int unlink(const char *pathname);
+int isatty(int fd);
+int execv(const char *path, char *const argv[]);
+int execve(const char *path, char *const argv[], char *const envp[]);
+int execvp(const char *file, char *const argv[]);
+int execl(const char *path, const char *arg, ...);
+int execlp(const char *file, const char *arg, ...);
+int execle(const char *path, const char *arg, ...);
+pid_t waitpid(pid_t pid, int *status, int options);
+
+#endif
diff --git a/src/userland/sys/beep.c b/src/userland/sys/beep.c
index 7ae4355..5745bcb 100644
--- a/src/userland/sys/beep.c
+++ b/src/userland/sys/beep.c
@@ -6,7 +6,7 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
- sys_system(14, 392, 400, 0, 0);
+ sys_system(SYSTEM_CMD_BEEP, 392, 400, 0, 0);
printf("BEEP!\n");
return 0;
}
diff --git a/src/userland/sys/clock.c b/src/userland/sys/clock.c
index 882cb08..a733c7d 100644
--- a/src/userland/sys/clock.c
+++ b/src/userland/sys/clock.c
@@ -40,6 +40,10 @@ static long long sw_start_ticks = 0;
static long long sw_elapsed_ticks = 0;
static bool sw_running = false;
+static long long sys_ticks_now(void) {
+ return sys_system(SYSTEM_CMD_GET_TICKS, 0, 0, 0, 0);
+}
+
static void format_time(char *buf, int h, int m, int s) {
buf[0] = '0' + (h / 10);
buf[1] = '0' + (h % 10);
@@ -55,7 +59,7 @@ static void format_time(char *buf, int h, int m, int s) {
static void update_rtc_state(void) {
int dt[6];
int old_s = cur_s;
- sys_system(11, (uint64_t)dt, 0, 0, 0);
+ sys_system(SYSTEM_CMD_RTC_GET, (uint64_t)dt, 0, 0, 0);
cur_y = dt[0]; cur_mon = dt[1]; cur_d = dt[2];
cur_h = dt[3]; cur_m = dt[4]; cur_s = dt[5];
@@ -123,7 +127,7 @@ static void clock_paint(void) {
draw_btn((WIN_W - 120) / 2, 195, 120, 28, "Apply changes", false);
} else if (current_tab == TAB_TIMER) {
- long long now = sys_system(16, 0, 0, 0, 0);
+ long long now = sys_ticks_now();
int display_h = timer_h, display_m = timer_m, display_s = timer_s;
if (timer_running) {
@@ -131,7 +135,7 @@ static void clock_paint(void) {
if (rem <= 0) {
timer_running = false;
display_h = display_m = display_s = 0;
- for(int i=0; i<3; i++) sys_system(14, 440, 200, 0, 0);
+ for (int i = 0; i < 3; i++) sys_system(SYSTEM_CMD_BEEP, 440, 200, 0, 0);
} else {
int s = rem / 60;
display_h = s / 3600;
@@ -157,7 +161,7 @@ static void clock_paint(void) {
} else if (current_tab == TAB_STOPWATCH) {
long long elapsed = sw_elapsed_ticks;
- if (sw_running) elapsed += (sys_system(16, 0, 0, 0, 0) - sw_start_ticks);
+ if (sw_running) elapsed += (sys_ticks_now() - sw_start_ticks);
int ms_val = (elapsed % 60) * 1000 / 60;
int s_val = (elapsed / 60) % 60;
@@ -207,7 +211,7 @@ static void clock_click(int x, int y) {
else if (x >= ax_clock + 73 && x <= ax_clock + 110) adj_s = (adj_s + 59) % 60;
} else if (x >= (WIN_W - 120) / 2 && x <= (WIN_W + 120) / 2 && y >= 195 && y <= 223) {
int dt[6] = {cur_y, cur_mon, cur_d, adj_h, adj_m, adj_s};
- sys_system(32, (uint64_t)dt, 0, 0, 0);
+ sys_system(SYSTEM_CMD_RTC_SET, (uint64_t)dt, 0, 0, 0);
}
} else if (current_tab == TAB_TIMER) {
if (!timer_running) {
@@ -225,16 +229,16 @@ static void clock_click(int x, int y) {
if (!timer_running) {
if (timer_h > 0 || timer_m > 0 || timer_s > 0) {
timer_running = true;
- timer_end_ticks = sys_system(16, 0, 0, 0, 0) + (long long)(timer_h * 3600 + timer_m * 60 + timer_s) * 60;
+ timer_end_ticks = sys_ticks_now() + (long long)(timer_h * 3600 + timer_m * 60 + timer_s) * 60;
}
} else timer_running = false;
}
} else if (current_tab == TAB_STOPWATCH) {
if (x >= 20 && x <= 110 && y >= 150 && y <= 180) {
- if (sw_running) { sw_elapsed_ticks += (sys_system(16, 0, 0, 0, 0) - sw_start_ticks); sw_running = false; }
- else { sw_start_ticks = sys_system(16, 0, 0, 0, 0); sw_running = true; }
+ if (sw_running) { sw_elapsed_ticks += (sys_ticks_now() - sw_start_ticks); sw_running = false; }
+ else { sw_start_ticks = sys_ticks_now(); sw_running = true; }
} else if (x >= 140 && x <= 230 && y >= 150 && y <= 180) {
- sw_elapsed_ticks = 0; sw_start_ticks = sys_system(16, 0, 0, 0, 0);
+ sw_elapsed_ticks = 0; sw_start_ticks = sys_ticks_now();
}
}
clock_paint();
@@ -254,7 +258,7 @@ int main(void) {
else if (ev.type == GUI_EVENT_CLICK) clock_click(ev.arg1, ev.arg2);
else if (ev.type == GUI_EVENT_CLOSE) sys_exit(0);
} else {
- long long now = sys_system(16, 0, 0, 0, 0);
+ long long now = sys_ticks_now();
if (now - last_rep >= 6) { clock_paint(); ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H); last_rep = now; }
sleep(10);
}
diff --git a/src/userland/sys/meminfo.c b/src/userland/sys/meminfo.c
index 1e11822..fa4f15f 100644
--- a/src/userland/sys/meminfo.c
+++ b/src/userland/sys/meminfo.c
@@ -7,7 +7,7 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
uint64_t mem[2];
- if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) {
+ if (sys_system(SYSTEM_CMD_GET_MEM_INFO, (uint64_t)mem, 0, 0, 0) == 0) {
printf("Memory Info:\n");
printf("Total: %d MB\n", (int)(mem[0] / 1024 / 1024));
printf("Used: %d MB\n", (int)(mem[1] / 1024 / 1024));
diff --git a/src/userland/sys/pci_list.c b/src/userland/sys/pci_list.c
index faeeabc..1198f60 100644
--- a/src/userland/sys/pci_list.c
+++ b/src/userland/sys/pci_list.c
@@ -13,7 +13,7 @@ typedef struct {
int main(int argc, char **argv) {
(void)argc; (void)argv;
- int count = sys_system(17, 0, 0, 0, 0);
+ int count = sys_system(SYSTEM_CMD_PCI_LIST, 0, 0, 0, 0);
if (count < 0) {
printf("Error: Could not retrieve PCI device count.\n");
return 1;
@@ -23,7 +23,7 @@ int main(int argc, char **argv) {
printf("---------------------------\n");
for (int i = 0; i < count; i++) {
pci_info_t info;
- if (sys_system(17, (uint64_t)&info, i, 0, 0) == 0) {
+ if (sys_system(SYSTEM_CMD_PCI_LIST, (uint64_t)&info, i, 0, 0) == 0) {
printf("[%d] Vendor:%04x Device:%04x Class:%02x Sub:%02x\n",
i, info.vendor, info.device, info.class_code, info.subclass);
}
diff --git a/src/userland/sys/reboot.c b/src/userland/sys/reboot.c
index 214afd6..edf5eea 100644
--- a/src/userland/sys/reboot.c
+++ b/src/userland/sys/reboot.c
@@ -7,6 +7,6 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
printf("Rebooting...\n");
- sys_system(12, 0, 0, 0, 0); // SYSTEM_CMD_REBOOT
+ sys_system(SYSTEM_CMD_REBOOT, 0, 0, 0, 0);
return 0;
}
diff --git a/src/userland/sys/shutdown.c b/src/userland/sys/shutdown.c
index 8825593..f13bfcd 100644
--- a/src/userland/sys/shutdown.c
+++ b/src/userland/sys/shutdown.c
@@ -7,6 +7,6 @@
int main(int argc, char **argv) {
(void)argc; (void)argv;
printf("Shutting down...\n");
- sys_system(13, 0, 0, 0, 0); // SYSTEM_CMD_SHUTDOWN
+ sys_system(SYSTEM_CMD_SHUTDOWN, 0, 0, 0, 0);
return 0;
}
diff --git a/src/wm/wm.c b/src/wm/wm.c
index 5f16fb9..2137981 100644
--- a/src/wm/wm.c
+++ b/src/wm/wm.c
@@ -246,6 +246,9 @@ static uint32_t timer_ticks = 0;
static int last_cursor_x = 400;
static int last_cursor_y = 300;
+#define CURSOR_W 18
+#define CURSOR_H 18
+
static bool periodic_refresh_pending = false;
// --- Desktop State ---
@@ -1344,27 +1347,38 @@ void draw_window(Window *win) {
}
}
-// Draw Mouse Cursor (Simple Arrow)
+// Draw Mouse Cursor (classic outlined arrow)
void draw_cursor(int x, int y) {
- // 0 = Transparent (skip), 1 = Black, 2 = White
- static const uint8_t cursor_bitmap[10][10] = {
- {1,1,0,0,0,0,0,0,0,0},
- {1,2,1,0,0,0,0,0,0,0},
- {1,2,2,1,0,0,0,0,0,0},
- {1,2,2,2,1,0,0,0,0,0},
- {1,2,2,2,2,1,0,0,0,0},
- {1,2,2,2,2,2,1,0,0,0},
- {1,2,2,1,1,1,1,0,0,0},
- {1,1,1,0,1,2,1,0,0,0},
- {0,0,0,0,0,1,2,1,0,0},
- {0,0,0,0,0,0,1,0,0,0}
+ // '.' transparent, 'w' white outline, 'b' black fill
+ static const char cursor_bitmap[CURSOR_H][CURSOR_W + 1] = {
+ "w.................",
+ "ww................",
+ "wbw...............",
+ "wbbw..............",
+ "wbbbw.............",
+ "wbbbbw............",
+ "wbbbbbw...........",
+ "wbbbbbbw..........",
+ "wbbbbbbbw.........",
+ "wbbbbbbbbw........",
+ "wbbbbbbbbw........",
+ "wbbbbbbbw.........",
+ "wbbbbbbbw.........",
+ "wbbbbbbw..........",
+ "wwwwbbbw..........",
+ "....wbbw..........",
+ ".....wbw..........",
+ "......ww..........."
};
-
- for (int r = 0; r < 10; r++) {
- for (int c = 0; c < 10; c++) {
- uint8_t p = cursor_bitmap[r][c];
- if (p == 1) put_pixel(x + c, y + r, COLOR_BLACK);
- else if (p == 2) put_pixel(x + c, y + r, COLOR_WHITE);
+
+ for (int r = 0; r < CURSOR_H; r++) {
+ for (int c = 0; c < CURSOR_W; c++) {
+ char p = cursor_bitmap[r][c];
+ if (p == 'w') {
+ put_pixel(x + c, y + r, COLOR_WHITE);
+ } else if (p == 'b') {
+ put_pixel(x + c, y + r, COLOR_BLACK);
+ }
}
}
}
@@ -1377,8 +1391,8 @@ static void erase_cursor(int x, int y) {
// Clamp to screen
int x1 = x < 0 ? 0 : x;
int y1 = y < 0 ? 0 : y;
- int x2 = x + 10 > sw ? sw : x + 10;
- int y2 = y + 10 > sh ? sh : y + 10;
+ int x2 = x + CURSOR_W > sw ? sw : x + CURSOR_W;
+ int y2 = y + CURSOR_H > sh ? sh : y + CURSOR_H;
int w = x2 - x1;
int h = y2 - y1;
@@ -1750,8 +1764,8 @@ void wm_paint(void) {
int sh = get_screen_height();
uint64_t rflags;
rflags = wm_lock_acquire();
- wm_mark_dirty(last_cursor_x, last_cursor_y, 12, 12);
- wm_mark_dirty(mx, my, 12, 12);
+ wm_mark_dirty(last_cursor_x, last_cursor_y, CURSOR_W, CURSOR_H);
+ wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H);
DirtyRect dirty = graphics_get_dirty_rect();
if (menubar_dirty_pending) {
@@ -2294,8 +2308,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (my >= sh) my = sh - 1;
if (move_x != 0 || move_y != 0) {
- wm_mark_dirty(prev_mx, prev_my, 12, 12); // Extra padding for safety
- wm_mark_dirty(mx, my, 12, 12);
+ wm_mark_dirty(prev_mx, prev_my, CURSOR_W, CURSOR_H);
+ wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H);
}
if (dz != 0) {
@@ -2897,8 +2911,8 @@ static void wm_handle_mouse_internal(int dx, int dy, uint8_t buttons, int dz) {
if (prev_mx != mx || prev_my != my) {
// Cursor moved - just mark dirty cursor areas
- wm_mark_dirty(prev_mx, prev_my, 10, 10);
- wm_mark_dirty(mx, my, 10, 10);
+ wm_mark_dirty(prev_mx, prev_my, CURSOR_W, CURSOR_H);
+ wm_mark_dirty(mx, my, CURSOR_W, CURSOR_H);
}
prev_left = left;