#define EMSGSIZE 90
#define EROFS 30
#define EWOULDBLOCK EAGAIN
+#define ENOPROTOOPT 92
+#define EOVERFLOW 75
+#define ELOOP 40
#endif
uintptr_t heap_start;
uintptr_t heap_break;
+ /* POSIX resource limits */
+#define RLIMIT_CPU 0
+#define RLIMIT_FSIZE 1
+#define RLIMIT_DATA 2
+#define RLIMIT_STACK 3
+#define RLIMIT_CORE 4
+#define RLIMIT_NOFILE 5
+#define RLIMIT_AS 6
+#define RLIMIT_NPROC 7
+#define _RLIMIT_COUNT 8
+#define RLIM_INFINITY 0xFFFFFFFFU
+ struct { uint32_t rlim_cur; uint32_t rlim_max; } rlimits[_RLIMIT_COUNT];
+
char cwd[128];
char cmdline[128];
uint32_t umask;
int ksocket_recvfrom(int sid, void* buf, size_t len, int flags,
struct sockaddr_in* src);
int ksocket_close(int sid);
+int ksocket_setsockopt(int sid, int level, int optname, const void* optval, uint32_t optlen);
+int ksocket_getsockopt(int sid, int level, int optname, void* optval, uint32_t* optlen);
+int ksocket_shutdown(int sid, int how);
+int ksocket_getpeername(int sid, struct sockaddr_in* addr);
+int ksocket_getsockname(int sid, struct sockaddr_in* addr);
int ksocket_poll(int sid, int events);
void ksocket_init(void);
SYSCALL_GETTIMEOFDAY = 127,
SYSCALL_MPROTECT = 128,
+ SYSCALL_GETRLIMIT = 129,
+ SYSCALL_SETRLIMIT = 130,
+ SYSCALL_SETSOCKOPT = 131,
+ SYSCALL_GETSOCKOPT = 132,
+ SYSCALL_SHUTDOWN = 133,
+ SYSCALL_GETPEERNAME = 134,
+ SYSCALL_GETSOCKNAME = 135,
};
#endif
current_process->addr_space = user_as;
current_process->heap_start = heap_brk;
current_process->heap_break = heap_brk;
+
+ /* Initialize default resource limits */
+ for (int i = 0; i < _RLIMIT_COUNT; i++) {
+ current_process->rlimits[i].rlim_cur = RLIM_INFINITY;
+ current_process->rlimits[i].rlim_max = RLIM_INFINITY;
+ }
+ current_process->rlimits[RLIMIT_NOFILE].rlim_cur = PROCESS_MAX_FILES;
+ current_process->rlimits[RLIMIT_NOFILE].rlim_max = PROCESS_MAX_FILES;
+ current_process->rlimits[RLIMIT_STACK].rlim_cur = 8 * 1024 * 1024; /* 8MB */
+ current_process->rlimits[RLIMIT_STACK].rlim_max = RLIM_INFINITY;
+ current_process->rlimits[RLIMIT_CORE].rlim_cur = 0; /* no core dumps */
+ current_process->rlimits[RLIMIT_CORE].rlim_max = 0;
strncpy(current_process->cmdline, init_path, sizeof(current_process->cmdline) - 1);
current_process->cmdline[sizeof(current_process->cmdline) - 1] = '\0';
vmm_as_activate(user_as);
proc->egid = current_process->egid;
proc->heap_start = current_process->heap_start;
proc->heap_break = current_process->heap_break;
+ memcpy(proc->rlimits, current_process->rlimits, sizeof(proc->rlimits));
memcpy(proc->fpu_state, current_process->fpu_state, FPU_STATE_SIZE);
return 0;
}
+int ksocket_setsockopt(int sid, int level, int optname, const void* optval, uint32_t optlen) {
+ struct ksocket* s = get_socket(sid);
+ if (!s) return -EBADF;
+
+ /* SOL_SOCKET level */
+ if (level == 1) { /* SOL_SOCKET */
+ if (optname == 2 /* SO_REUSEADDR */ && optlen >= 4) {
+ /* lwIP tcp_bind already allows reuse; just accept the call */
+ (void)optval;
+ return 0;
+ }
+ if (optname == 9 /* SO_KEEPALIVE */ && s->type == SOCK_STREAM && optlen >= 4) {
+ int val = *(const int*)optval;
+ if (s->pcb.tcp) {
+ if (val) s->pcb.tcp->so_options |= SOF_KEEPALIVE;
+ else s->pcb.tcp->so_options &= (uint8_t)~SOF_KEEPALIVE;
+ }
+ return 0;
+ }
+ /* SO_RCVBUF / SO_SNDBUF — accept but ignore (fixed buffers) */
+ if ((optname == 8 /* SO_RCVBUF */ || optname == 7 /* SO_SNDBUF */) && optlen >= 4) {
+ return 0;
+ }
+ }
+
+ return -ENOPROTOOPT;
+}
+
+int ksocket_getsockopt(int sid, int level, int optname, void* optval, uint32_t* optlen) {
+ struct ksocket* s = get_socket(sid);
+ if (!s) return -EBADF;
+
+ if (level == 1) { /* SOL_SOCKET */
+ if (optname == 4 /* SO_ERROR */ && *optlen >= 4) {
+ int err = s->error;
+ s->error = 0;
+ *(int*)optval = err;
+ *optlen = 4;
+ return 0;
+ }
+ if (optname == 3 /* SO_TYPE */ && *optlen >= 4) {
+ *(int*)optval = s->type;
+ *optlen = 4;
+ return 0;
+ }
+ }
+
+ return -ENOPROTOOPT;
+}
+
+int ksocket_shutdown(int sid, int how) {
+ struct ksocket* s = get_socket(sid);
+ if (!s) return -EBADF;
+ if (s->type != SOCK_STREAM || !s->pcb.tcp) return -ENOTCONN;
+
+ /* how: 0=SHUT_RD, 1=SHUT_WR, 2=SHUT_RDWR */
+ if (how == 1 || how == 2) {
+ tcp_shutdown(s->pcb.tcp, 0, 1); /* shut_rx=0, shut_tx=1 */
+ }
+ if (how == 0 || how == 2) {
+ s->state = KSOCK_PEER_CLOSED;
+ wq_wake_all(&s->rx_wq);
+ }
+ return 0;
+}
+
+int ksocket_getpeername(int sid, struct sockaddr_in* addr) {
+ struct ksocket* s = get_socket(sid);
+ if (!s) return -EBADF;
+ if (s->type == SOCK_STREAM && s->pcb.tcp && s->state == KSOCK_CONNECTED) {
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(s->pcb.tcp->remote_port);
+ addr->sin_addr = ip_addr_get_ip4_u32(&s->pcb.tcp->remote_ip);
+ return 0;
+ }
+ return -ENOTCONN;
+}
+
+int ksocket_getsockname(int sid, struct sockaddr_in* addr) {
+ struct ksocket* s = get_socket(sid);
+ if (!s) return -EBADF;
+ if (s->type == SOCK_STREAM && s->pcb.tcp) {
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(s->pcb.tcp->local_port);
+ addr->sin_addr = ip_addr_get_ip4_u32(&s->pcb.tcp->local_ip);
+ return 0;
+ }
+ if (s->type == SOCK_DGRAM && s->pcb.udp) {
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(s->pcb.udp->local_port);
+ addr->sin_addr = ip_addr_get_ip4_u32(&s->pcb.udp->local_ip);
+ return 0;
+ }
+ return -EINVAL;
+}
+
int ksocket_poll(int sid, int events) {
if (sid < 0 || sid >= KSOCKET_MAX) return VFS_POLL_ERR;
struct ksocket* s = &sockets[sid];
return;
}
+ if (syscall_no == SYSCALL_GETRLIMIT) {
+ uint32_t resource = sc_arg0(regs);
+ void* user_rlim = (void*)sc_arg1(regs);
+ if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+ if (resource >= _RLIMIT_COUNT) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+ if (!user_rlim || user_range_ok(user_rlim, 8) == 0) {
+ sc_ret(regs) = (uint32_t)-EFAULT; return;
+ }
+ if (copy_to_user(user_rlim, ¤t_process->rlimits[resource], 8) < 0) {
+ sc_ret(regs) = (uint32_t)-EFAULT; return;
+ }
+ sc_ret(regs) = 0;
+ return;
+ }
+
+ if (syscall_no == SYSCALL_SETRLIMIT) {
+ uint32_t resource = sc_arg0(regs);
+ const void* user_rlim = (const void*)sc_arg1(regs);
+ if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+ if (resource >= _RLIMIT_COUNT) { sc_ret(regs) = (uint32_t)-EINVAL; return; }
+ if (!user_rlim || user_range_ok(user_rlim, 8) == 0) {
+ sc_ret(regs) = (uint32_t)-EFAULT; return;
+ }
+ struct { uint32_t cur; uint32_t max; } new_rl;
+ if (copy_from_user(&new_rl, user_rlim, 8) < 0) {
+ sc_ret(regs) = (uint32_t)-EFAULT; return;
+ }
+ /* Non-root cannot raise max above current max */
+ if (new_rl.max > current_process->rlimits[resource].rlim_max &&
+ current_process->euid != 0) {
+ sc_ret(regs) = (uint32_t)-EPERM; return;
+ }
+ if (new_rl.cur > new_rl.max) new_rl.cur = new_rl.max;
+ current_process->rlimits[resource].rlim_cur = new_rl.cur;
+ current_process->rlimits[resource].rlim_max = new_rl.max;
+ sc_ret(regs) = 0;
+ return;
+ }
+
if (syscall_no == SYSCALL_MPROTECT) {
uintptr_t addr = (uintptr_t)sc_arg0(regs);
uint32_t len = sc_arg1(regs);
return;
}
+ if (syscall_no == SYSCALL_SETSOCKOPT) {
+ int sid = sock_fd_get_sid((int)sc_arg0(regs));
+ if (sid < 0) { sc_ret(regs) = (uint32_t)-EBADF; return; }
+ int level = (int)sc_arg1(regs);
+ int optname = (int)sc_arg2(regs);
+ uint32_t optlen = sc_arg4(regs);
+ int kval = 0;
+ if (optlen >= 4 && sc_arg3(regs)) {
+ if (copy_from_user(&kval, (const void*)sc_arg3(regs), 4) < 0) {
+ sc_ret(regs) = (uint32_t)-EFAULT; return;
+ }
+ }
+ sc_ret(regs) = (uint32_t)ksocket_setsockopt(sid, level, optname, &kval, optlen);
+ return;
+ }
+
+ if (syscall_no == SYSCALL_GETSOCKOPT) {
+ int sid = sock_fd_get_sid((int)sc_arg0(regs));
+ if (sid < 0) { sc_ret(regs) = (uint32_t)-EBADF; return; }
+ int level = (int)sc_arg1(regs);
+ int optname = (int)sc_arg2(regs);
+ int kval = 0;
+ uint32_t klen = 4;
+ int r = ksocket_getsockopt(sid, level, optname, &kval, &klen);
+ if (r == 0 && sc_arg3(regs) && sc_arg4(regs)) {
+ if (copy_to_user((void*)sc_arg3(regs), &kval, 4) < 0) {
+ sc_ret(regs) = (uint32_t)-EFAULT; return;
+ }
+ (void)copy_to_user((void*)sc_arg4(regs), &klen, 4);
+ }
+ sc_ret(regs) = (uint32_t)r;
+ return;
+ }
+
+ if (syscall_no == SYSCALL_SHUTDOWN) {
+ int sid = sock_fd_get_sid((int)sc_arg0(regs));
+ if (sid < 0) { sc_ret(regs) = (uint32_t)-EBADF; return; }
+ sc_ret(regs) = (uint32_t)ksocket_shutdown(sid, (int)sc_arg1(regs));
+ return;
+ }
+
+ if (syscall_no == SYSCALL_GETPEERNAME) {
+ int sid = sock_fd_get_sid((int)sc_arg0(regs));
+ if (sid < 0) { sc_ret(regs) = (uint32_t)-EBADF; return; }
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ int r = ksocket_getpeername(sid, &sa);
+ if (r == 0 && sc_arg1(regs)) {
+ (void)copy_to_user((void*)sc_arg1(regs), &sa, sizeof(sa));
+ }
+ sc_ret(regs) = (uint32_t)r;
+ return;
+ }
+
+ if (syscall_no == SYSCALL_GETSOCKNAME) {
+ int sid = sock_fd_get_sid((int)sc_arg0(regs));
+ if (sid < 0) { sc_ret(regs) = (uint32_t)-EBADF; return; }
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ int r = ksocket_getsockname(sid, &sa);
+ if (r == 0 && sc_arg1(regs)) {
+ (void)copy_to_user((void*)sc_arg1(regs), &sa, sizeof(sa));
+ }
+ sc_ret(regs) = (uint32_t)r;
+ return;
+ }
+
sc_ret(regs) = (uint32_t)-ENOSYS;
}
--- /dev/null
+#ifndef ULIBC_FNMATCH_H
+#define ULIBC_FNMATCH_H
+
+#define FNM_NOMATCH 1
+#define FNM_PATHNAME (1 << 0)
+#define FNM_NOESCAPE (1 << 1)
+#define FNM_PERIOD (1 << 2)
+
+int fnmatch(const char* pattern, const char* string, int flags);
+
+#endif
--- /dev/null
+#ifndef ULIBC_GETOPT_H
+#define ULIBC_GETOPT_H
+
+extern char* optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+int getopt(int argc, char* const argv[], const char* optstring);
+
+struct option {
+ const char* name;
+ int has_arg;
+ int* flag;
+ int val;
+};
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+int getopt_long(int argc, char* const argv[], const char* optstring,
+ const struct option* longopts, int* longindex);
+
+#endif
--- /dev/null
+#ifndef ULIBC_GRP_H
+#define ULIBC_GRP_H
+
+#include <stddef.h>
+
+struct group {
+ char* gr_name;
+ char* gr_passwd;
+ int gr_gid;
+ char** gr_mem;
+};
+
+struct group* getgrnam(const char* name);
+struct group* getgrgid(int gid);
+void setgrent(void);
+void endgrent(void);
+struct group* getgrent(void);
+
+#endif
--- /dev/null
+#ifndef ULIBC_LOCALE_H
+#define ULIBC_LOCALE_H
+
+#define LC_ALL 0
+#define LC_COLLATE 1
+#define LC_CTYPE 2
+#define LC_MONETARY 3
+#define LC_NUMERIC 4
+#define LC_TIME 5
+#define LC_MESSAGES 6
+
+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;
+};
+
+char* setlocale(int category, const char* locale);
+struct lconv* localeconv(void);
+
+#endif
--- /dev/null
+#ifndef ULIBC_PWD_H
+#define ULIBC_PWD_H
+
+#include <stddef.h>
+
+struct passwd {
+ char* pw_name;
+ char* pw_passwd;
+ int pw_uid;
+ int pw_gid;
+ char* pw_gecos;
+ char* pw_dir;
+ char* pw_shell;
+};
+
+struct passwd* getpwnam(const char* name);
+struct passwd* getpwuid(int uid);
+void setpwent(void);
+void endpwent(void);
+struct passwd* getpwent(void);
+
+#endif
--- /dev/null
+#ifndef ULIBC_SETJMP_H
+#define ULIBC_SETJMP_H
+
+/* jmp_buf layout for i386:
+ * [0] EBX [1] ESI [2] EDI [3] EBP [4] ESP [5] EIP
+ */
+typedef unsigned long jmp_buf[6];
+typedef unsigned long sigjmp_buf[6 + 1 + 1]; /* +saved_sigmask +sigmask */
+
+int setjmp(jmp_buf env);
+void longjmp(jmp_buf env, int val) __attribute__((noreturn));
+int sigsetjmp(sigjmp_buf env, int savesigs);
+void siglongjmp(sigjmp_buf env, int val) __attribute__((noreturn));
+
+int _setjmp(jmp_buf env);
+void _longjmp(jmp_buf env, int val) __attribute__((noreturn));
+
+#endif
#define _IONBF 2 /* unbuffered */
int setvbuf(FILE* fp, char* buf, int mode, size_t size);
void setbuf(FILE* fp, char* buf);
+void perror(const char* s);
+int fileno(FILE* fp);
+FILE* fdopen(int fd, const char* mode);
#endif
int atoi(const char* s);
double atof(const char* s);
long strtol(const char* nptr, char** endptr, int base);
+unsigned long strtoul(const char* nptr, char** endptr, int base);
+long long strtoll(const char* nptr, char** endptr, int base);
+unsigned long long strtoull(const char* nptr, char** endptr, int base);
char* realpath(const char* path, char* resolved);
char* getenv(const char* name);
int abs(int x);
long labs(long x);
+int setenv(const char* name, const char* value, int overwrite);
+int unsetenv(const char* name);
+int putenv(char* string);
+int atexit(void (*func)(void));
+void abort(void) __attribute__((noreturn));
+int rand(void);
+void srand(unsigned int seed);
+#define RAND_MAX 0x7FFF
void qsort(void* base, size_t nmemb, size_t size,
int (*compar)(const void*, const void*));
char* strstr(const char* haystack, const char* needle);
void* memchr(const void* s, int c, size_t n);
char* strtok(char* str, const char* delim);
+char* strtok_r(char* str, const char* delim, char** saveptr);
+char* strerror(int errnum);
+size_t strnlen(const char* s, size_t maxlen);
+size_t strspn(const char* s, const char* accept);
+size_t strcspn(const char* s, const char* reject);
+char* strpbrk(const char* s, const char* accept);
#endif
--- /dev/null
+#ifndef ULIBC_SYS_RESOURCE_H
+#define ULIBC_SYS_RESOURCE_H
+
+#include <stdint.h>
+
+#define RLIMIT_CPU 0
+#define RLIMIT_FSIZE 1
+#define RLIMIT_DATA 2
+#define RLIMIT_STACK 3
+#define RLIMIT_CORE 4
+#define RLIMIT_NOFILE 5
+#define RLIMIT_AS 6
+#define RLIMIT_NPROC 7
+#define RLIM_INFINITY 0xFFFFFFFFU
+
+typedef uint32_t rlim_t;
+
+struct rlimit {
+ rlim_t rlim_cur;
+ rlim_t rlim_max;
+};
+
+int getrlimit(int resource, struct rlimit *rlim);
+int setrlimit(int resource, const struct rlimit *rlim);
+
+#endif
--- /dev/null
+#ifndef ULIBC_SYS_SELECT_H
+#define ULIBC_SYS_SELECT_H
+
+#include <stdint.h>
+#include "sys/time.h"
+
+#define FD_SETSIZE 64
+
+typedef struct {
+ uint32_t fds_bits[FD_SETSIZE / 32];
+} fd_set;
+
+#define FD_ZERO(set) do { for (int _i = 0; _i < (int)(FD_SETSIZE/32); _i++) (set)->fds_bits[_i] = 0; } while(0)
+#define FD_SET(fd, set) ((set)->fds_bits[(fd) / 32] |= (1U << ((fd) % 32)))
+#define FD_CLR(fd, set) ((set)->fds_bits[(fd) / 32] &= ~(1U << ((fd) % 32)))
+#define FD_ISSET(fd, set) ((set)->fds_bits[(fd) / 32] & (1U << ((fd) % 32)))
+
+int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
+ struct timeval* timeout);
+
+#endif
SYS_MOUNT = 126,
SYS_GETTIMEOFDAY = 127,
SYS_MPROTECT = 128,
+ SYS_GETRLIMIT = 129,
+ SYS_SETRLIMIT = 130,
+ SYS_SETSOCKOPT = 131,
+ SYS_GETSOCKOPT = 132,
+ SYS_SHUTDOWN = 133,
+ SYS_GETPEERNAME = 134,
+ SYS_GETSOCKNAME = 135,
};
/* Raw syscall wrappers — up to 5 args via INT 0x80 */
int readlink(const char* path, char* buf, size_t bufsiz);
int kill(int pid, int sig);
int rename(const char* oldpath, const char* newpath);
+unsigned int sleep(unsigned int seconds);
+int usleep(unsigned int usec);
+int execvp(const char* file, char* const argv[]);
+int execlp(const char* file, const char* arg, ...);
+int execl(const char* path, const char* arg, ...);
+int getopt(int argc, char* const argv[], const char* optstring);
+extern char* optarg;
+extern int optind, opterr, optopt;
void _exit(int status) __attribute__((noreturn));
--- /dev/null
+#include "stdlib.h"
+#include "signal.h"
+#include "unistd.h"
+
+static void (*_atexit_funcs[32])(void);
+static int _atexit_count = 0;
+
+int atexit(void (*func)(void)) {
+ if (_atexit_count >= 32) return -1;
+ _atexit_funcs[_atexit_count++] = func;
+ return 0;
+}
+
+void exit(int status) {
+ /* Call atexit handlers in reverse order */
+ for (int i = _atexit_count - 1; i >= 0; i--) {
+ if (_atexit_funcs[i]) _atexit_funcs[i]();
+ }
+ _exit(status);
+}
+
+void abort(void) {
+ raise(SIGABRT);
+ _exit(127);
+}
--- /dev/null
+#include "stdlib.h"
+#include "string.h"
+#include "errno.h"
+#include <stddef.h>
+
+extern char** __environ;
+
+static char* _env_storage[128];
+static int _env_owned = 0;
+
+static void _ensure_own_environ(void) {
+ if (_env_owned) return;
+ int count = 0;
+ if (__environ) {
+ for (; __environ[count]; count++) {
+ if (count >= 126) break;
+ _env_storage[count] = __environ[count];
+ }
+ }
+ _env_storage[count] = (char*)0;
+ __environ = _env_storage;
+ _env_owned = 1;
+}
+
+int setenv(const char* name, const char* value, int overwrite) {
+ if (!name || !*name || strchr(name, '=')) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ _ensure_own_environ();
+
+ size_t nlen = strlen(name);
+ /* Check if already exists */
+ for (int i = 0; __environ[i]; i++) {
+ if (strncmp(__environ[i], name, nlen) == 0 && __environ[i][nlen] == '=') {
+ if (!overwrite) return 0;
+ /* Replace in-place */
+ size_t vlen = strlen(value);
+ char* buf = malloc(nlen + 1 + vlen + 1);
+ if (!buf) { errno = ENOMEM; return -1; }
+ memcpy(buf, name, nlen);
+ buf[nlen] = '=';
+ memcpy(buf + nlen + 1, value, vlen + 1);
+ __environ[i] = buf;
+ return 0;
+ }
+ }
+
+ /* Count entries */
+ int count = 0;
+ while (__environ[count]) count++;
+ if (count >= 126) { errno = ENOMEM; return -1; }
+
+ size_t vlen = strlen(value);
+ char* buf = malloc(nlen + 1 + vlen + 1);
+ if (!buf) { errno = ENOMEM; return -1; }
+ memcpy(buf, name, nlen);
+ buf[nlen] = '=';
+ memcpy(buf + nlen + 1, value, vlen + 1);
+
+ __environ[count] = buf;
+ __environ[count + 1] = (char*)0;
+ return 0;
+}
+
+int unsetenv(const char* name) {
+ if (!name || !*name || strchr(name, '=')) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ _ensure_own_environ();
+
+ size_t nlen = strlen(name);
+ for (int i = 0; __environ[i]; i++) {
+ if (strncmp(__environ[i], name, nlen) == 0 && __environ[i][nlen] == '=') {
+ /* Shift remaining entries down */
+ for (int j = i; __environ[j]; j++)
+ __environ[j] = __environ[j + 1];
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int putenv(char* string) {
+ if (!string) { errno = EINVAL; return -1; }
+
+ _ensure_own_environ();
+
+ char* eq = strchr(string, '=');
+ if (!eq) return unsetenv(string);
+
+ size_t nlen = (size_t)(eq - string);
+
+ /* Replace existing or append */
+ for (int i = 0; __environ[i]; i++) {
+ if (strncmp(__environ[i], string, nlen + 1) == 0) {
+ __environ[i] = string;
+ return 0;
+ }
+ }
+
+ int count = 0;
+ while (__environ[count]) count++;
+ if (count >= 126) { errno = ENOMEM; return -1; }
+ __environ[count] = string;
+ __environ[count + 1] = (char*)0;
+ return 0;
+}
--- /dev/null
+#include "unistd.h"
+#include "string.h"
+#include "stdlib.h"
+#include "errno.h"
+#include <stddef.h>
+
+extern char** __environ;
+
+int execvp(const char* file, char* const argv[]) {
+ if (!file || !*file) { errno = ENOENT; return -1; }
+
+ /* If file contains '/', use it directly */
+ if (strchr(file, '/'))
+ return execve(file, argv, __environ);
+
+ /* Search PATH */
+ const char* path = getenv("PATH");
+ if (!path) path = "/bin:/usr/bin";
+
+ char buf[256];
+ size_t flen = strlen(file);
+
+ while (*path) {
+ const char* sep = path;
+ while (*sep && *sep != ':') sep++;
+ size_t dlen = (size_t)(sep - path);
+ if (dlen == 0) { dlen = 1; path = "."; }
+
+ if (dlen + 1 + flen + 1 > sizeof(buf)) {
+ path = *sep ? sep + 1 : sep;
+ continue;
+ }
+
+ memcpy(buf, path, dlen);
+ buf[dlen] = '/';
+ memcpy(buf + dlen + 1, file, flen + 1);
+
+ execve(buf, argv, __environ);
+ /* If ENOENT, try next; otherwise fail */
+ if (errno != ENOENT) return -1;
+
+ path = *sep ? sep + 1 : sep;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+int execlp(const char* file, const char* arg, ...) {
+ /* Count args by walking the va_list manually via stack pointers.
+ * On i386, args are pushed right-to-left on the stack.
+ * We'll use a simple approach: max 32 args. */
+ const char* args[33];
+ const char** p = &arg;
+ int i = 0;
+ while (*p && i < 32) {
+ args[i++] = *p;
+ p++;
+ }
+ args[i] = (const char*)0;
+ return execvp(file, (char* const*)args);
+}
+
+int execl(const char* path, const char* arg, ...) {
+ const char* args[33];
+ const char** p = &arg;
+ int i = 0;
+ while (*p && i < 32) {
+ args[i++] = *p;
+ p++;
+ }
+ args[i] = (const char*)0;
+ return execve(path, (char* const*)args, __environ);
+}
--- /dev/null
+#include "fnmatch.h"
+#include <stddef.h>
+
+int fnmatch(const char* pattern, const char* string, int flags) {
+ const char* p = pattern;
+ const char* s = string;
+
+ while (*p) {
+ if (*p == '*') {
+ p++;
+ /* Skip consecutive stars */
+ while (*p == '*') p++;
+ if (!*p) return 0; /* trailing * matches everything */
+ /* Try matching rest of pattern at each position */
+ while (*s) {
+ if (fnmatch(p, s, flags) == 0) return 0;
+ if ((flags & FNM_PATHNAME) && *s == '/') break;
+ s++;
+ }
+ return FNM_NOMATCH;
+ } else if (*p == '?') {
+ if (!*s) return FNM_NOMATCH;
+ if ((flags & FNM_PATHNAME) && *s == '/') return FNM_NOMATCH;
+ if ((flags & FNM_PERIOD) && *s == '.' && (s == string || ((flags & FNM_PATHNAME) && s[-1] == '/')))
+ return FNM_NOMATCH;
+ p++; s++;
+ } else if (*p == '[') {
+ if (!*s) return FNM_NOMATCH;
+ p++;
+ int negate = 0;
+ if (*p == '!' || *p == '^') { negate = 1; p++; }
+ int match = 0;
+ while (*p && *p != ']') {
+ char lo = *p++;
+ if (*p == '-' && p[1] && p[1] != ']') {
+ p++;
+ char hi = *p++;
+ if (*s >= lo && *s <= hi) match = 1;
+ } else {
+ if (*s == lo) match = 1;
+ }
+ }
+ if (*p == ']') p++;
+ if (negate) match = !match;
+ if (!match) return FNM_NOMATCH;
+ s++;
+ } else {
+ /* Literal character */
+ if (*p != *s) return FNM_NOMATCH;
+ p++; s++;
+ }
+ }
+
+ return (*s == '\0') ? 0 : FNM_NOMATCH;
+}
--- /dev/null
+#include "getopt.h"
+#include "string.h"
+#include <stddef.h>
+
+char* optarg = (char*)0;
+int optind = 1;
+int opterr = 1;
+int optopt = '?';
+
+static int _optpos = 0; /* position within current argv element */
+
+int getopt(int argc, char* const argv[], const char* optstring) {
+ if (optind >= argc || !argv[optind]) return -1;
+
+ const char* arg = argv[optind];
+
+ /* Skip non-options */
+ if (arg[0] != '-' || arg[1] == '\0') return -1;
+ if (arg[1] == '-' && arg[2] == '\0') { optind++; return -1; } /* "--" */
+
+ int pos = _optpos ? _optpos : 1;
+ int c = arg[pos];
+ _optpos = 0;
+
+ const char* match = strchr(optstring, c);
+ if (!match) {
+ optopt = c;
+ if (arg[pos + 1]) _optpos = pos + 1;
+ else optind++;
+ return '?';
+ }
+
+ if (match[1] == ':') {
+ /* Option requires argument */
+ if (arg[pos + 1]) {
+ optarg = (char*)&arg[pos + 1];
+ optind++;
+ } else if (optind + 1 < argc) {
+ optarg = argv[optind + 1];
+ optind += 2;
+ } else {
+ optopt = c;
+ optind++;
+ return (optstring[0] == ':') ? ':' : '?';
+ }
+ } else {
+ optarg = (char*)0;
+ if (arg[pos + 1]) {
+ _optpos = pos + 1;
+ } else {
+ optind++;
+ }
+ }
+
+ return c;
+}
+
+int getopt_long(int argc, char* const argv[], const char* optstring,
+ const struct option* longopts, int* longindex) {
+ if (optind >= argc || !argv[optind]) return -1;
+
+ const char* arg = argv[optind];
+
+ /* Handle long options: --name or --name=value */
+ if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0') {
+ const char* name = arg + 2;
+ const char* eq = strchr(name, '=');
+ size_t nlen = eq ? (size_t)(eq - name) : strlen(name);
+
+ for (int i = 0; longopts[i].name; i++) {
+ if (strncmp(longopts[i].name, name, nlen) == 0 &&
+ longopts[i].name[nlen] == '\0') {
+ if (longindex) *longindex = i;
+
+ if (longopts[i].has_arg == required_argument || longopts[i].has_arg == optional_argument) {
+ if (eq) {
+ optarg = (char*)(eq + 1);
+ } else if (longopts[i].has_arg == required_argument && optind + 1 < argc) {
+ optarg = argv[optind + 1];
+ optind++;
+ } else if (longopts[i].has_arg == required_argument) {
+ optind++;
+ return '?';
+ } else {
+ optarg = (char*)0;
+ }
+ } else {
+ optarg = (char*)0;
+ }
+
+ optind++;
+ if (longopts[i].flag) {
+ *longopts[i].flag = longopts[i].val;
+ return 0;
+ }
+ return longopts[i].val;
+ }
+ }
+ optind++;
+ return '?';
+ }
+
+ /* Fall back to short option parsing */
+ return getopt(argc, argv, optstring);
+}
--- /dev/null
+#include "locale.h"
+#include <stddef.h>
+
+static struct lconv _c_lconv = {
+ .decimal_point = ".",
+ .thousands_sep = "",
+ .grouping = "",
+ .int_curr_symbol = "",
+ .currency_symbol = "",
+ .mon_decimal_point = "",
+ .mon_thousands_sep = "",
+ .mon_grouping = "",
+ .positive_sign = "",
+ .negative_sign = "",
+ .int_frac_digits = 127,
+ .frac_digits = 127,
+ .p_cs_precedes = 127,
+ .p_sep_by_space = 127,
+ .n_cs_precedes = 127,
+ .n_sep_by_space = 127,
+ .p_sign_posn = 127,
+ .n_sign_posn = 127,
+};
+
+char* setlocale(int category, const char* locale) {
+ (void)category;
+ (void)locale;
+ return "C";
+}
+
+struct lconv* localeconv(void) {
+ return &_c_lconv;
+}
--- /dev/null
+#include "pwd.h"
+#include "grp.h"
+#include <stddef.h>
+
+/* Minimal /etc/passwd and /etc/group stubs.
+ * AdrOS has a single-user model (uid=0 root). */
+
+static struct passwd _root = {
+ .pw_name = "root",
+ .pw_passwd = "x",
+ .pw_uid = 0,
+ .pw_gid = 0,
+ .pw_gecos = "root",
+ .pw_dir = "/",
+ .pw_shell = "/bin/sh",
+};
+
+static int _pw_idx = 0;
+
+struct passwd* getpwnam(const char* name) {
+ if (!name) return (struct passwd*)0;
+ /* strcmp inline to avoid header dependency issues */
+ const char* a = name;
+ const char* b = "root";
+ while (*a && *a == *b) { a++; b++; }
+ if (*a == *b) return &_root;
+ return (struct passwd*)0;
+}
+
+struct passwd* getpwuid(int uid) {
+ if (uid == 0) return &_root;
+ return (struct passwd*)0;
+}
+
+void setpwent(void) { _pw_idx = 0; }
+void endpwent(void) { _pw_idx = 0; }
+
+struct passwd* getpwent(void) {
+ if (_pw_idx == 0) { _pw_idx++; return &_root; }
+ return (struct passwd*)0;
+}
+
+static char* _root_members[] = { "root", (char*)0 };
+
+static struct group _root_grp = {
+ .gr_name = "root",
+ .gr_passwd = "x",
+ .gr_gid = 0,
+ .gr_mem = _root_members,
+};
+
+static int _gr_idx = 0;
+
+struct group* getgrnam(const char* name) {
+ if (!name) return (struct group*)0;
+ const char* a = name;
+ const char* b = "root";
+ while (*a && *a == *b) { a++; b++; }
+ if (*a == *b) return &_root_grp;
+ return (struct group*)0;
+}
+
+struct group* getgrgid(int gid) {
+ if (gid == 0) return &_root_grp;
+ return (struct group*)0;
+}
+
+void setgrent(void) { _gr_idx = 0; }
+void endgrent(void) { _gr_idx = 0; }
+
+struct group* getgrent(void) {
+ if (_gr_idx == 0) { _gr_idx++; return &_root_grp; }
+ return (struct group*)0;
+}
--- /dev/null
+#include "stdlib.h"
+
+static unsigned long _rand_seed = 1;
+
+void srand(unsigned int seed) {
+ _rand_seed = seed;
+}
+
+int rand(void) {
+ _rand_seed = _rand_seed * 1103515245UL + 12345UL;
+ return (int)((_rand_seed >> 16) & 0x7FFF);
+}
+
+#define RAND_MAX 0x7FFF
--- /dev/null
+#include "sys/resource.h"
+#include "syscall.h"
+#include "errno.h"
+
+int getrlimit(int resource, struct rlimit *rlim) {
+ return __syscall_ret(_syscall2(SYS_GETRLIMIT, resource, (int)rlim));
+}
+
+int setrlimit(int resource, const struct rlimit *rlim) {
+ return __syscall_ret(_syscall2(SYS_SETRLIMIT, resource, (int)rlim));
+}
--- /dev/null
+/*
+ * setjmp / longjmp for i386
+ *
+ * jmp_buf layout: [0]=EBX [1]=ESI [2]=EDI [3]=EBP [4]=ESP [5]=EIP
+ */
+.text
+
+.global setjmp
+.global _setjmp
+.global longjmp
+.global _longjmp
+.global sigsetjmp
+.global siglongjmp
+
+/* int setjmp(jmp_buf env) */
+setjmp:
+_setjmp:
+ mov 4(%esp), %eax /* eax = env */
+ mov %ebx, 0(%eax)
+ mov %esi, 4(%eax)
+ mov %edi, 8(%eax)
+ mov %ebp, 12(%eax)
+ lea 4(%esp), %ecx /* caller's ESP (before call pushed retaddr) */
+ mov %ecx, 16(%eax)
+ mov (%esp), %ecx /* return address = caller's EIP */
+ mov %ecx, 20(%eax)
+ xor %eax, %eax /* return 0 */
+ ret
+
+/* void longjmp(jmp_buf env, int val) */
+longjmp:
+_longjmp:
+ mov 4(%esp), %edx /* edx = env */
+ mov 8(%esp), %eax /* eax = val */
+ test %eax, %eax
+ jnz 1f
+ inc %eax /* if val==0, return 1 */
+1:
+ mov 0(%edx), %ebx
+ mov 4(%edx), %esi
+ mov 8(%edx), %edi
+ mov 12(%edx), %ebp
+ mov 16(%edx), %esp
+ jmp *20(%edx) /* jump to saved EIP */
+
+/* int sigsetjmp(sigjmp_buf env, int savesigs) — minimal, no signal mask save */
+sigsetjmp:
+ mov 4(%esp), %eax /* eax = env */
+ mov %ebx, 0(%eax)
+ mov %esi, 4(%eax)
+ mov %edi, 8(%eax)
+ mov %ebp, 12(%eax)
+ lea 4(%esp), %ecx
+ mov %ecx, 16(%eax)
+ mov (%esp), %ecx
+ mov %ecx, 20(%eax)
+ /* env[6] = savesigs flag */
+ mov 8(%esp), %ecx
+ mov %ecx, 24(%eax)
+ /* env[7] = 0 (signal mask placeholder) */
+ movl $0, 28(%eax)
+ xor %eax, %eax
+ ret
+
+/* void siglongjmp(sigjmp_buf env, int val) */
+siglongjmp:
+ mov 4(%esp), %edx
+ mov 8(%esp), %eax
+ test %eax, %eax
+ jnz 1f
+ inc %eax
+1:
+ mov 0(%edx), %ebx
+ mov 4(%edx), %esi
+ mov 8(%edx), %edi
+ mov 12(%edx), %ebp
+ mov 16(%edx), %esp
+ jmp *20(%edx)
+
+.section .note.GNU-stack,"",@progbits
--- /dev/null
+#include "signal.h"
+
+typedef void (*sighandler_t)(int);
+
+sighandler_t signal(int signum, sighandler_t handler) {
+ struct sigaction sa, old;
+ sa.sa_handler = (uintptr_t)handler;
+ sa.sa_mask = 0;
+ sa.sa_flags = SA_RESTART;
+ if (sigaction(signum, &sa, &old) < 0)
+ return (sighandler_t)-1; /* SIG_ERR */
+ return (sighandler_t)(uintptr_t)old.sa_handler;
+}
--- /dev/null
+#include "unistd.h"
+#include "time.h"
+#include "errno.h"
+
+unsigned int sleep(unsigned int seconds) {
+ struct timespec req = { .tv_sec = seconds, .tv_nsec = 0 };
+ struct timespec rem = { .tv_sec = 0, .tv_nsec = 0 };
+ if (nanosleep(&req, &rem) < 0) {
+ return (unsigned int)rem.tv_sec;
+ }
+ return 0;
+}
+
+int usleep(unsigned int usec) {
+ struct timespec req;
+ req.tv_sec = usec / 1000000;
+ req.tv_nsec = (usec % 1000000) * 1000;
+ return nanosleep(&req, (void*)0);
+}
char** __environ = 0;
/*
- * Minimal bump allocator using brk() syscall.
- * No free() support yet — memory is only reclaimed on process exit.
- * A proper free-list allocator can be added later.
+ * Free-list allocator using brk() syscall.
+ *
+ * Each allocated block has a header: [size | in_use_flag]
+ * Free blocks are linked in an explicit free list.
+ * Coalescing is done on free() (merge with adjacent free blocks).
+ *
+ * Layout: [header 8 bytes] [user data ...]
+ * header.size = total block size including header (aligned to 8)
+ * header.next = pointer to next free block (only valid when free)
*/
+
+#define ALLOC_ALIGN 8
+#define ALLOC_HDR_SIZE 8 /* must == sizeof(struct block_hdr) */
+#define ALLOC_USED_BIT 1U
+
+struct block_hdr {
+ uint32_t size; /* total size including header; bit 0 = used flag */
+ uint32_t next_free; /* pointer to next free block (as uintptr_t), 0 = end */
+};
+
+static struct block_hdr* free_list = 0;
static void* heap_base = 0;
static void* heap_end = 0;
-void* malloc(size_t size) {
- if (size == 0) return (void*)0;
-
- /* Align to 8 bytes */
- size = (size + 7) & ~(size_t)7;
+static inline uint32_t blk_size(struct block_hdr* b) { return b->size & ~ALLOC_USED_BIT; }
+static inline int blk_used(struct block_hdr* b) { return (int)(b->size & ALLOC_USED_BIT); }
+static void* sbrk_grow(size_t inc) {
if (!heap_base) {
heap_base = brk(0);
heap_end = heap_base;
}
-
void* old_end = heap_end;
- void* new_end = (void*)((char*)heap_end + size);
+ void* new_end = (void*)((char*)heap_end + inc);
void* result = brk(new_end);
+ if ((uintptr_t)result < (uintptr_t)new_end)
+ return (void*)0;
+ heap_end = new_end;
+ return old_end;
+}
+
+void* malloc(size_t size) {
+ if (size == 0) return (void*)0;
- if ((uintptr_t)result < (uintptr_t)new_end) {
- return (void*)0; /* OOM */
+ /* Align to 8 bytes, add header */
+ size = (size + ALLOC_ALIGN - 1) & ~(size_t)(ALLOC_ALIGN - 1);
+ uint32_t total = (uint32_t)size + ALLOC_HDR_SIZE;
+
+ /* First-fit search in free list */
+ struct block_hdr** prev = &free_list;
+ struct block_hdr* cur = free_list;
+ while (cur) {
+ uint32_t bsz = blk_size(cur);
+ if (bsz >= total) {
+ /* Split if remainder is large enough for another block */
+ if (bsz >= total + ALLOC_HDR_SIZE + ALLOC_ALIGN) {
+ struct block_hdr* split = (struct block_hdr*)((char*)cur + total);
+ split->size = bsz - total;
+ split->next_free = cur->next_free;
+ *prev = split;
+ cur->size = total | ALLOC_USED_BIT;
+ } else {
+ /* Use entire block */
+ *prev = (struct block_hdr*)(uintptr_t)cur->next_free;
+ cur->size = bsz | ALLOC_USED_BIT;
+ }
+ return (void*)((char*)cur + ALLOC_HDR_SIZE);
+ }
+ prev = (struct block_hdr**)&cur->next_free;
+ cur = (struct block_hdr*)(uintptr_t)cur->next_free;
}
- heap_end = new_end;
- return old_end;
+ /* No free block found — grow heap */
+ void* p = sbrk_grow(total);
+ if (!p) return (void*)0;
+ struct block_hdr* b = (struct block_hdr*)p;
+ b->size = total | ALLOC_USED_BIT;
+ b->next_free = 0;
+ return (void*)((char*)b + ALLOC_HDR_SIZE);
}
void free(void* ptr) {
- /* Bump allocator: no-op for now */
- (void)ptr;
+ if (!ptr) return;
+ struct block_hdr* b = (struct block_hdr*)((char*)ptr - ALLOC_HDR_SIZE);
+ b->size &= ~ALLOC_USED_BIT; /* mark free */
+
+ /* Insert into free list (address-ordered for coalescing) */
+ struct block_hdr** prev = &free_list;
+ struct block_hdr* cur = free_list;
+ while (cur && (uintptr_t)cur < (uintptr_t)b) {
+ prev = (struct block_hdr**)&cur->next_free;
+ cur = (struct block_hdr*)(uintptr_t)cur->next_free;
+ }
+
+ b->next_free = (uint32_t)(uintptr_t)cur;
+ *prev = b;
+
+ /* Coalesce with next block if adjacent */
+ if (cur && (char*)b + blk_size(b) == (char*)cur) {
+ b->size += blk_size(cur);
+ b->next_free = cur->next_free;
+ }
+
+ /* Coalesce with previous block if adjacent */
+ struct block_hdr* p_prev = free_list;
+ if (p_prev != b) {
+ while (p_prev && (struct block_hdr*)(uintptr_t)p_prev->next_free != b)
+ p_prev = (struct block_hdr*)(uintptr_t)p_prev->next_free;
+ if (p_prev && (char*)p_prev + blk_size(p_prev) == (char*)b) {
+ p_prev->size += blk_size(b);
+ p_prev->next_free = b->next_free;
+ }
+ }
}
void* calloc(size_t nmemb, size_t size) {
void* realloc(void* ptr, size_t size) {
if (!ptr) return malloc(size);
if (size == 0) { free(ptr); return (void*)0; }
- /* Bump allocator: just allocate new and copy.
- * We don't know the old size, so copy 'size' bytes
- * (caller must ensure old block >= size). */
+
+ struct block_hdr* b = (struct block_hdr*)((char*)ptr - ALLOC_HDR_SIZE);
+ uint32_t old_usable = blk_size(b) - ALLOC_HDR_SIZE;
+
+ if (old_usable >= size) return ptr; /* already large enough */
+
void* new_ptr = malloc(size);
- if (new_ptr) memcpy(new_ptr, ptr, size);
+ if (new_ptr) {
+ memcpy(new_ptr, ptr, old_usable);
+ free(ptr);
+ }
return new_ptr;
}
(void)cmd;
return -1;
}
-
-void exit(int status) {
- _exit(status);
-}
--- /dev/null
+#include "string.h"
+#include "stdio.h"
+#include "errno.h"
+
+static const char* _err_table[] = {
+ [0] = "Success",
+ [EPERM] = "Operation not permitted",
+ [ENOENT] = "No such file or directory",
+ [ESRCH] = "No such process",
+ [EINTR] = "Interrupted system call",
+ [EIO] = "Input/output error",
+ [E2BIG] = "Argument list too long",
+ [EBADF] = "Bad file descriptor",
+ [ECHILD] = "No child processes",
+ [EAGAIN] = "Resource temporarily unavailable",
+ [ENOMEM] = "Cannot allocate memory",
+ [EACCES] = "Permission denied",
+ [EFAULT] = "Bad address",
+ [EBUSY] = "Device or resource busy",
+ [EEXIST] = "File exists",
+ [ENODEV] = "No such device",
+ [ENOTDIR] = "Not a directory",
+ [EISDIR] = "Is a directory",
+ [EINVAL] = "Invalid argument",
+ [EMFILE] = "Too many open files",
+ [ENOTTY] = "Inappropriate ioctl for device",
+ [ENOSPC] = "No space left on device",
+ [ESPIPE] = "Illegal seek",
+ [EROFS] = "Read-only file system",
+ [EPIPE] = "Broken pipe",
+ [ERANGE] = "Numerical result out of range",
+ [ENAMETOOLONG] = "File name too long",
+ [ENOLCK] = "No locks available",
+ [ENOSYS] = "Function not implemented",
+ [ENOTEMPTY] = "Directory not empty",
+};
+
+#define ERR_TABLE_SIZE (int)(sizeof(_err_table) / sizeof(_err_table[0]))
+
+static char _unknown_buf[32];
+
+char* strerror(int errnum) {
+ if (errnum >= 0 && errnum < ERR_TABLE_SIZE && _err_table[errnum])
+ return (char*)_err_table[errnum];
+ /* Build "Unknown error NNN" */
+ char* p = _unknown_buf;
+ const char* prefix = "Unknown error ";
+ while (*prefix) *p++ = *prefix++;
+ /* itoa inline */
+ int n = errnum < 0 ? -errnum : errnum;
+ char tmp[12];
+ int i = 0;
+ if (errnum < 0) *p++ = '-';
+ do { tmp[i++] = '0' + (n % 10); n /= 10; } while (n > 0);
+ while (i > 0) *p++ = tmp[--i];
+ *p = '\0';
+ return _unknown_buf;
+}
+
+void perror(const char* s) {
+ if (s && *s) {
+ fputs(s, stderr);
+ fputs(": ", stderr);
+ }
+ fputs(strerror(errno), stderr);
+ fputc('\n', stderr);
+}
static char* strtok_state = (void*)0;
char* strtok(char* str, const char* delim) {
- if (str) strtok_state = str;
- if (!strtok_state) return (void*)0;
+ return strtok_r(str, delim, &strtok_state);
+}
+
+char* strtok_r(char* str, const char* delim, char** saveptr) {
+ if (str) *saveptr = str;
+ if (!*saveptr) return (void*)0;
/* skip leading delimiters */
- while (*strtok_state && strchr(delim, *strtok_state)) strtok_state++;
- if (!*strtok_state) return (void*)0;
- char* start = strtok_state;
- while (*strtok_state && !strchr(delim, *strtok_state)) strtok_state++;
- if (*strtok_state) *strtok_state++ = 0;
+ while (**saveptr && strchr(delim, **saveptr)) (*saveptr)++;
+ if (!**saveptr) return (void*)0;
+ char* start = *saveptr;
+ while (**saveptr && !strchr(delim, **saveptr)) (*saveptr)++;
+ if (**saveptr) *(*saveptr)++ = 0;
return start;
}
+
+size_t strnlen(const char* s, size_t maxlen) {
+ size_t i = 0;
+ while (i < maxlen && s[i]) i++;
+ return i;
+}
+
+size_t strspn(const char* s, const char* accept) {
+ size_t i = 0;
+ while (s[i] && strchr(accept, s[i])) i++;
+ return i;
+}
+
+size_t strcspn(const char* s, const char* reject) {
+ size_t i = 0;
+ while (s[i] && !strchr(reject, s[i])) i++;
+ return i;
+}
+
+char* strpbrk(const char* s, const char* accept) {
+ while (*s) {
+ if (strchr(accept, *s)) return (char*)s;
+ s++;
+ }
+ return (void*)0;
+}
--- /dev/null
+#include "stdlib.h"
+#include "ctype.h"
+#include "errno.h"
+#include <stdint.h>
+
+unsigned long strtoul(const char* nptr, char** endptr, int base) {
+ const char* s = nptr;
+ unsigned long result = 0;
+ int neg = 0;
+
+ while (isspace(*s)) s++;
+ if (*s == '-') { neg = 1; s++; }
+ else if (*s == '+') { s++; }
+
+ if (base == 0) {
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
+ else if (s[0] == '0') { base = 8; s++; }
+ else { base = 10; }
+ } else if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s += 2;
+ }
+
+ int overflow = 0;
+ while (*s) {
+ int digit;
+ if (*s >= '0' && *s <= '9') digit = *s - '0';
+ else if (*s >= 'a' && *s <= 'z') digit = *s - 'a' + 10;
+ else if (*s >= 'A' && *s <= 'Z') digit = *s - 'A' + 10;
+ else break;
+ if (digit >= base) break;
+ if (result > (0xFFFFFFFFUL - (unsigned long)digit) / (unsigned long)base)
+ overflow = 1;
+ result = result * (unsigned long)base + (unsigned long)digit;
+ s++;
+ }
+
+ if (endptr) *endptr = (char*)s;
+ if (overflow) { errno = ERANGE; return 0xFFFFFFFFUL; }
+ return neg ? (unsigned long)(-(long)result) : result;
+}
+
+long long strtoll(const char* nptr, char** endptr, int base) {
+ const char* s = nptr;
+ long long result = 0;
+ int neg = 0;
+
+ while (isspace(*s)) s++;
+ if (*s == '-') { neg = 1; s++; }
+ else if (*s == '+') { s++; }
+
+ if (base == 0) {
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
+ else if (s[0] == '0') { base = 8; s++; }
+ else { base = 10; }
+ } else if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s += 2;
+ }
+
+ while (*s) {
+ int digit;
+ if (*s >= '0' && *s <= '9') digit = *s - '0';
+ else if (*s >= 'a' && *s <= 'z') digit = *s - 'a' + 10;
+ else if (*s >= 'A' && *s <= 'Z') digit = *s - 'A' + 10;
+ else break;
+ if (digit >= base) break;
+ result = result * base + digit;
+ s++;
+ }
+
+ if (endptr) *endptr = (char*)s;
+ return neg ? -result : result;
+}
+
+unsigned long long strtoull(const char* nptr, char** endptr, int base) {
+ const char* s = nptr;
+ unsigned long long result = 0;
+ int neg = 0;
+
+ while (isspace(*s)) s++;
+ if (*s == '-') { neg = 1; s++; }
+ else if (*s == '+') { s++; }
+
+ if (base == 0) {
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; }
+ else if (s[0] == '0') { base = 8; s++; }
+ else { base = 10; }
+ } else if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s += 2;
+ }
+
+ while (*s) {
+ int digit;
+ if (*s >= '0' && *s <= '9') digit = *s - '0';
+ else if (*s >= 'a' && *s <= 'z') digit = *s - 'a' + 10;
+ else if (*s >= 'A' && *s <= 'Z') digit = *s - 'A' + 10;
+ else break;
+ if (digit >= base) break;
+ result = result * (unsigned long long)base + (unsigned long long)digit;
+ s++;
+ }
+
+ if (endptr) *endptr = (char*)s;
+ return neg ? (unsigned long long)(-(long long)result) : result;
+}