]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives
authorTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 12:54:07 +0000 (09:54 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 14 Mar 2026 12:54:07 +0000 (09:54 -0300)
Tier 1: Add ENOTSOCK/ENETUNREACH errno codes (kernel + ulibc)

Tier 2: 13 new ulibc wrapper headers + source files:
  - sys/socket.h, netinet/in.h, arpa/inet.h, netdb.h, sys/un.h
  - sys/epoll.h, sys/inotify.h, dlfcn.h, spawn.h
  - semaphore.h, mqueue.h, aio.h, sys/shm.h
  - All with syscall wrappers matching kernel syscall numbers

Tier 3: Missing functions in existing headers:
  - termios: cfmakeraw, cfget/setispeed, tcdrain, tcflush, tcgetpgrp/tcsetpgrp
  - string: strsignal
  - stdlib: bsearch, div/ldiv, mkstemp, strtod/strtof
  - stdio: scanf, fscanf, tmpfile, tmpnam
  - unistd: execle, getlogin, confstr, sbrk, link/symlink/readlink wrappers
  - New syslog.h stub (openlog/syslog/closelog)

Tier 4: Kernel syscalls:
  - umount2 (SYSCALL_UMOUNT2=138) with vfs_umount in fs.c
  - wait4 (SYSCALL_WAIT4=139) wrapping process_waitpid + rusage

Tier 5: Medium implementations:
  - glob.h/wordexp.h with directory walk + fnmatch pattern matching
  - pthread_mutex/cond/rwlock/key/once/barrier (futex-based)
  - /etc/passwd + /etc/group file parsing with static root fallback

Build: make clean && make OK, cppcheck clean, 101/101 smoke tests pass

50 files changed:
include/errno.h
include/fs.h
include/syscall.h
src/kernel/fs.c
src/kernel/syscall.c
user/ulibc/include/aio.h [new file with mode: 0644]
user/ulibc/include/arpa/inet.h [new file with mode: 0644]
user/ulibc/include/dlfcn.h [new file with mode: 0644]
user/ulibc/include/errno.h
user/ulibc/include/glob.h [new file with mode: 0644]
user/ulibc/include/mqueue.h [new file with mode: 0644]
user/ulibc/include/netdb.h [new file with mode: 0644]
user/ulibc/include/netinet/in.h [new file with mode: 0644]
user/ulibc/include/pthread.h
user/ulibc/include/semaphore.h [new file with mode: 0644]
user/ulibc/include/spawn.h [new file with mode: 0644]
user/ulibc/include/stdio.h
user/ulibc/include/stdlib.h
user/ulibc/include/string.h
user/ulibc/include/sys/epoll.h [new file with mode: 0644]
user/ulibc/include/sys/inotify.h [new file with mode: 0644]
user/ulibc/include/sys/shm.h [new file with mode: 0644]
user/ulibc/include/sys/socket.h [new file with mode: 0644]
user/ulibc/include/sys/un.h [new file with mode: 0644]
user/ulibc/include/syscall.h
user/ulibc/include/syslog.h [new file with mode: 0644]
user/ulibc/include/termios.h
user/ulibc/include/unistd.h
user/ulibc/include/wordexp.h [new file with mode: 0644]
user/ulibc/src/aio.c [new file with mode: 0644]
user/ulibc/src/dlfcn.c [new file with mode: 0644]
user/ulibc/src/epoll.c [new file with mode: 0644]
user/ulibc/src/glob.c [new file with mode: 0644]
user/ulibc/src/inet.c [new file with mode: 0644]
user/ulibc/src/inotify.c [new file with mode: 0644]
user/ulibc/src/mqueue.c [new file with mode: 0644]
user/ulibc/src/netdb.c [new file with mode: 0644]
user/ulibc/src/pthread.c
user/ulibc/src/pwd_grp.c
user/ulibc/src/semaphore.c [new file with mode: 0644]
user/ulibc/src/shm.c [new file with mode: 0644]
user/ulibc/src/socket.c [new file with mode: 0644]
user/ulibc/src/spawn.c [new file with mode: 0644]
user/ulibc/src/stdio.c
user/ulibc/src/stdlib.c
user/ulibc/src/string.c
user/ulibc/src/syslog.c [new file with mode: 0644]
user/ulibc/src/termios.c [new file with mode: 0644]
user/ulibc/src/unistd.c
user/ulibc/src/wordexp.c [new file with mode: 0644]

index 580d3b301b96ef0b8365263ba92a96219a6387f7..c4e067d84105d7aab9215a72e6c157b97392160f 100644 (file)
@@ -43,5 +43,7 @@
 #define ENOPROTOOPT 92
 #define EOVERFLOW 75
 #define ELOOP 40
+#define ENOTSOCK 88
+#define ENETUNREACH 101
 
 #endif
index 69411bb2a38048f87441bbdf7bddb83cfd08e97a..096dd2b896d66337813452a8c55bc42113fd0a7c 100644 (file)
@@ -86,6 +86,7 @@ int vfs_truncate(const char* path, uint32_t length);
 int vfs_link(const char* old_path, const char* new_path);
 
 int vfs_mount(const char* mountpoint, fs_node_t* root);
+int vfs_umount(const char* mountpoint);
 
 // Global root of the filesystem
 extern fs_node_t* fs_root;
index 2b998d65e1096cccfc640b0317c7cb0066eb92e2..78b5f0c2278bba7515fdcf6d1162c994e975597a 100644 (file)
@@ -168,6 +168,8 @@ enum {
     SYSCALL_GETSOCKNAME  = 135,
     SYSCALL_UNAME        = 136,
     SYSCALL_GETRUSAGE    = 137,
+    SYSCALL_UMOUNT2      = 138,
+    SYSCALL_WAIT4        = 139,
 };
 
 #endif
index f04fc2d82fa6d0b7c211ffd51ab7a474e89b9718..5d007e05b534846b4257569e9dd4e268cdd9a15a 100644 (file)
@@ -69,6 +69,23 @@ int vfs_mount(const char* mountpoint, fs_node_t* root) {
     return 0;
 }
 
+int vfs_umount(const char* mountpoint) {
+    char mp[128];
+    normalize_mountpoint(mountpoint, mp, sizeof(mp));
+
+    if (strcmp(mp, "/") == 0) return -EBUSY;
+
+    for (int i = 0; i < g_mount_count; i++) {
+        if (strcmp(g_mounts[i].mountpoint, mp) == 0) {
+            for (int j = i; j < g_mount_count - 1; j++)
+                g_mounts[j] = g_mounts[j + 1];
+            g_mount_count--;
+            return 0;
+        }
+    }
+    return -EINVAL;
+}
+
 uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
     if (node->f_ops && node->f_ops->read)
         return node->f_ops->read(node, offset, size, buffer);
index 64b40bb6410f2a6f4978ca592e99c24a6aceeb5a..d12f6fa3757d204188a6d676607a8fa2b86d654e 100644 (file)
@@ -4934,6 +4934,50 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no)
         return;
     }
 
+    if (syscall_no == SYSCALL_UMOUNT2) {
+        if (!current_process || current_process->euid != 0) {
+            sc_ret(regs) = (uint32_t)-EPERM;
+            return;
+        }
+        const char* user_target = (const char*)sc_arg0(regs);
+        char ktarget[128];
+        if (path_resolve_user(user_target, ktarget, sizeof(ktarget)) < 0) {
+            sc_ret(regs) = (uint32_t)-EFAULT;
+            return;
+        }
+        sc_ret(regs) = (uint32_t)vfs_umount(ktarget);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_WAIT4) {
+        int pid     = (int)sc_arg0(regs);
+        int* ustatus = (int*)sc_arg1(regs);
+        int options = (int)sc_arg2(regs);
+        void* urusage = (void*)sc_arg3(regs);
+
+        if (ustatus && user_range_ok(ustatus, sizeof(int)) == 0) {
+            sc_ret(regs) = (uint32_t)-EFAULT; return;
+        }
+
+        int kstatus = 0;
+        int ret = process_waitpid(pid, &kstatus, options);
+        if (ret < 0) {
+            sc_ret(regs) = (uint32_t)ret;
+            return;
+        }
+        if (ret > 0 && ustatus) {
+            (void)copy_to_user(ustatus, &kstatus, sizeof(kstatus));
+        }
+        if (ret > 0 && urusage) {
+            /* Zero out rusage — detailed accounting deferred */
+            char zeros[64];
+            memset(zeros, 0, sizeof(zeros));
+            (void)copy_to_user(urusage, zeros, sizeof(zeros));
+        }
+        sc_ret(regs) = (uint32_t)ret;
+        return;
+    }
+
     sc_ret(regs) = (uint32_t)-ENOSYS;
 }
 
diff --git a/user/ulibc/include/aio.h b/user/ulibc/include/aio.h
new file mode 100644 (file)
index 0000000..2054d82
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef ULIBC_AIO_H
+#define ULIBC_AIO_H
+
+#include <stddef.h>
+#include <signal.h>
+#include <time.h>
+
+struct aiocb {
+    int             aio_fildes;
+    volatile void*  aio_buf;
+    size_t          aio_nbytes;
+    int             aio_offset;
+    int             aio_reqprio;
+    int             aio_lio_opcode;
+    /* internal */
+    int             __error;
+    int             __return;
+};
+
+#define AIO_CANCELED    0
+#define AIO_NOTCANCELED 1
+#define AIO_ALLDONE     2
+
+int aio_read(struct aiocb* aiocbp);
+int aio_write(struct aiocb* aiocbp);
+int aio_error(const struct aiocb* aiocbp);
+int aio_return(struct aiocb* aiocbp);
+int aio_suspend(const struct aiocb* const list[], int nent,
+                const struct timespec* timeout);
+int aio_cancel(int fd, struct aiocb* aiocbp);
+
+#endif
diff --git a/user/ulibc/include/arpa/inet.h b/user/ulibc/include/arpa/inet.h
new file mode 100644 (file)
index 0000000..9dd3733
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef ULIBC_ARPA_INET_H
+#define ULIBC_ARPA_INET_H
+
+#include <stdint.h>
+#include <netinet/in.h>
+
+in_addr_t   inet_addr(const char* cp);
+int         inet_aton(const char* cp, struct in_addr* inp);
+char*       inet_ntoa(struct in_addr in);
+const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
+int         inet_pton(int af, const char* src, void* dst);
+
+#endif
diff --git a/user/ulibc/include/dlfcn.h b/user/ulibc/include/dlfcn.h
new file mode 100644 (file)
index 0000000..81414c5
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef ULIBC_DLFCN_H
+#define ULIBC_DLFCN_H
+
+#define RTLD_LAZY   0x01
+#define RTLD_NOW    0x02
+#define RTLD_GLOBAL 0x100
+#define RTLD_LOCAL  0x000
+
+void* dlopen(const char* filename, int flags);
+void* dlsym(void* handle, const char* symbol);
+int   dlclose(void* handle);
+char* dlerror(void);
+
+#endif
index 688664232b4b55c047de8075be6d3f47ada64904..506c98b1963f7a997cb54df6be769a52d6f6a63e 100644 (file)
@@ -45,6 +45,8 @@ extern int errno;
 #define ENOPROTOOPT     92
 #define EPROTONOSUPPORT 93
 #define EOPNOTSUPP      95
+#define ENOTSOCK        88
+#define ENETUNREACH     101
 #define EWOULDBLOCK     EAGAIN
 
 /* Convert raw syscall return to errno-style */
diff --git a/user/ulibc/include/glob.h b/user/ulibc/include/glob.h
new file mode 100644 (file)
index 0000000..572b16d
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef ULIBC_GLOB_H
+#define ULIBC_GLOB_H
+
+#include <stddef.h>
+
+#define GLOB_ERR      0x01
+#define GLOB_MARK     0x02
+#define GLOB_NOSORT   0x04
+#define GLOB_DOOFFS   0x08
+#define GLOB_NOCHECK  0x10
+#define GLOB_APPEND   0x20
+#define GLOB_NOESCAPE 0x40
+
+#define GLOB_NOSPACE  1
+#define GLOB_ABORTED  2
+#define GLOB_NOMATCH  3
+
+typedef struct {
+    size_t  gl_pathc;
+    char**  gl_pathv;
+    size_t  gl_offs;
+    /* internal */
+    size_t  __alloc;
+} glob_t;
+
+int  glob(const char* pattern, int flags,
+          int (*errfunc)(const char*, int), glob_t* pglob);
+void globfree(glob_t* pglob);
+
+#endif
diff --git a/user/ulibc/include/mqueue.h b/user/ulibc/include/mqueue.h
new file mode 100644 (file)
index 0000000..08b33e2
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef ULIBC_MQUEUE_H
+#define ULIBC_MQUEUE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef int mqd_t;
+
+struct mq_attr {
+    long mq_flags;
+    long mq_maxmsg;
+    long mq_msgsize;
+    long mq_curmsgs;
+};
+
+mqd_t mq_open(const char* name, int oflag, ...);
+int   mq_close(mqd_t mqdes);
+int   mq_send(mqd_t mqdes, const char* msg_ptr, size_t msg_len, unsigned int msg_prio);
+int   mq_receive(mqd_t mqdes, char* msg_ptr, size_t msg_len, unsigned int* msg_prio);
+int   mq_unlink(const char* name);
+int   mq_getattr(mqd_t mqdes, struct mq_attr* attr);
+int   mq_setattr(mqd_t mqdes, const struct mq_attr* newattr, struct mq_attr* oldattr);
+
+#endif
diff --git a/user/ulibc/include/netdb.h b/user/ulibc/include/netdb.h
new file mode 100644 (file)
index 0000000..08ac345
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef ULIBC_NETDB_H
+#define ULIBC_NETDB_H
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+struct addrinfo {
+    int              ai_flags;
+    int              ai_family;
+    int              ai_socktype;
+    int              ai_protocol;
+    socklen_t        ai_addrlen;
+    struct sockaddr* ai_addr;
+    char*            ai_canonname;
+    struct addrinfo* ai_next;
+};
+
+struct hostent {
+    char*  h_name;
+    char** h_aliases;
+    int    h_addrtype;
+    int    h_length;
+    char** h_addr_list;
+};
+
+#define AI_PASSIVE     0x01
+#define AI_CANONNAME   0x02
+#define AI_NUMERICHOST 0x04
+#define AI_NUMERICSERV 0x08
+
+#define NI_MAXHOST 1025
+#define NI_MAXSERV 32
+
+#define EAI_NONAME  -2
+#define EAI_AGAIN   -3
+#define EAI_FAIL    -4
+#define EAI_FAMILY  -6
+#define EAI_SOCKTYPE -7
+#define EAI_SERVICE -8
+#define EAI_MEMORY  -10
+#define EAI_SYSTEM  -11
+
+int getaddrinfo(const char* node, const char* service,
+                const struct addrinfo* hints, struct addrinfo** res);
+void freeaddrinfo(struct addrinfo* res);
+const char* gai_strerror(int errcode);
+
+#endif
diff --git a/user/ulibc/include/netinet/in.h b/user/ulibc/include/netinet/in.h
new file mode 100644 (file)
index 0000000..d7f0b7c
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef ULIBC_NETINET_IN_H
+#define ULIBC_NETINET_IN_H
+
+#include <stdint.h>
+#include <sys/socket.h>
+
+typedef uint16_t in_port_t;
+typedef uint32_t in_addr_t;
+
+struct in_addr {
+    in_addr_t s_addr;
+};
+
+struct sockaddr_in {
+    sa_family_t    sin_family;
+    in_port_t      sin_port;
+    struct in_addr sin_addr;
+    unsigned char  sin_zero[8];
+};
+
+struct in6_addr {
+    uint8_t s6_addr[16];
+};
+
+struct sockaddr_in6 {
+    sa_family_t     sin6_family;
+    in_port_t       sin6_port;
+    uint32_t        sin6_flowinfo;
+    struct in6_addr sin6_addr;
+    uint32_t        sin6_scope_id;
+};
+
+#define INADDR_ANY       ((in_addr_t)0x00000000)
+#define INADDR_BROADCAST ((in_addr_t)0xFFFFFFFF)
+#define INADDR_LOOPBACK  ((in_addr_t)0x7F000001)
+#define INADDR_NONE      ((in_addr_t)0xFFFFFFFF)
+
+#define IPPROTO_IP   0
+#define IPPROTO_ICMP 1
+#define IPPROTO_TCP  6
+#define IPPROTO_UDP  17
+#define IPPROTO_RAW  255
+
+static inline uint16_t htons(uint16_t x) {
+    return (uint16_t)((x >> 8) | (x << 8));
+}
+static inline uint16_t ntohs(uint16_t x) { return htons(x); }
+
+static inline uint32_t htonl(uint32_t x) {
+    return ((x >> 24) & 0xFF) | ((x >> 8) & 0xFF00) |
+           ((x << 8) & 0xFF0000) | ((x << 24) & 0xFF000000U);
+}
+static inline uint32_t ntohl(uint32_t x) { return htonl(x); }
+
+#endif
index ac5ecefb5a91f56d27ac1fdc809ea030aad101d1..fba453ec0e490f2b96a9156a04fb60eb4aecc106 100644 (file)
@@ -48,4 +48,117 @@ int pthread_attr_destroy(pthread_attr_t* attr);
 /* Set stack size in thread attributes. */
 int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize);
 
+/* Detach */
+int pthread_detach(pthread_t thread);
+
+/* Cancel */
+int pthread_cancel(pthread_t thread);
+int pthread_setcancelstate(int state, int* oldstate);
+int pthread_setcanceltype(int type, int* oldtype);
+void pthread_testcancel(void);
+
+#define PTHREAD_CANCEL_ENABLE  0
+#define PTHREAD_CANCEL_DISABLE 1
+#define PTHREAD_CANCEL_DEFERRED     0
+#define PTHREAD_CANCEL_ASYNCHRONOUS 1
+
+/* ---- Mutex ---- */
+typedef struct {
+    volatile int __lock;
+    int          __owner;
+    int          __type;
+    int          __count;
+} pthread_mutex_t;
+
+typedef struct {
+    int __type;
+} pthread_mutexattr_t;
+
+#define PTHREAD_MUTEX_NORMAL     0
+#define PTHREAD_MUTEX_RECURSIVE  1
+#define PTHREAD_MUTEX_ERRORCHECK 2
+#define PTHREAD_MUTEX_DEFAULT    PTHREAD_MUTEX_NORMAL
+
+#define PTHREAD_MUTEX_INITIALIZER { 0, 0, 0, 0 }
+
+int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);
+int pthread_mutex_destroy(pthread_mutex_t* mutex);
+int pthread_mutex_lock(pthread_mutex_t* mutex);
+int pthread_mutex_trylock(pthread_mutex_t* mutex);
+int pthread_mutex_unlock(pthread_mutex_t* mutex);
+
+int pthread_mutexattr_init(pthread_mutexattr_t* attr);
+int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
+int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type);
+int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type);
+
+/* ---- Condition Variable ---- */
+typedef struct {
+    volatile int __seq;
+} pthread_cond_t;
+
+typedef struct {
+    int __dummy;
+} pthread_condattr_t;
+
+#define PTHREAD_COND_INITIALIZER { 0 }
+
+int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr);
+int pthread_cond_destroy(pthread_cond_t* cond);
+int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
+int pthread_cond_signal(pthread_cond_t* cond);
+int pthread_cond_broadcast(pthread_cond_t* cond);
+
+/* ---- Read-Write Lock ---- */
+typedef struct {
+    volatile int __readers;
+    volatile int __writer;
+} pthread_rwlock_t;
+
+typedef struct {
+    int __dummy;
+} pthread_rwlockattr_t;
+
+#define PTHREAD_RWLOCK_INITIALIZER { 0, 0 }
+
+int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr);
+int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
+int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
+int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
+int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
+int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
+int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
+
+/* ---- Thread-Specific Data (Keys) ---- */
+typedef unsigned int pthread_key_t;
+
+int   pthread_key_create(pthread_key_t* key, void (*destructor)(void*));
+int   pthread_key_delete(pthread_key_t key);
+void* pthread_getspecific(pthread_key_t key);
+int   pthread_setspecific(pthread_key_t key, const void* value);
+
+/* ---- Once ---- */
+typedef volatile int pthread_once_t;
+#define PTHREAD_ONCE_INIT 0
+
+int pthread_once(pthread_once_t* once_control, void (*init_routine)(void));
+
+/* ---- Barrier (optional) ---- */
+typedef struct {
+    volatile int __count;
+    volatile int __total;
+    volatile int __seq;
+} pthread_barrier_t;
+
+typedef struct {
+    int __dummy;
+} pthread_barrierattr_t;
+
+#define PTHREAD_BARRIER_SERIAL_THREAD (-1)
+
+int pthread_barrier_init(pthread_barrier_t* barrier,
+                         const pthread_barrierattr_t* attr, unsigned count);
+int pthread_barrier_destroy(pthread_barrier_t* barrier);
+int pthread_barrier_wait(pthread_barrier_t* barrier);
+
 #endif
diff --git a/user/ulibc/include/semaphore.h b/user/ulibc/include/semaphore.h
new file mode 100644 (file)
index 0000000..ffc3cc7
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef ULIBC_SEMAPHORE_H
+#define ULIBC_SEMAPHORE_H
+
+#include <stdint.h>
+
+typedef struct {
+    int __val;
+} sem_t;
+
+#define SEM_FAILED ((sem_t*)-1)
+
+sem_t* sem_open(const char* name, int oflag, ...);
+int    sem_close(sem_t* sem);
+int    sem_wait(sem_t* sem);
+int    sem_trywait(sem_t* sem);
+int    sem_post(sem_t* sem);
+int    sem_unlink(const char* name);
+int    sem_getvalue(sem_t* sem, int* sval);
+int    sem_init(sem_t* sem, int pshared, unsigned int value);
+int    sem_destroy(sem_t* sem);
+
+#endif
diff --git a/user/ulibc/include/spawn.h b/user/ulibc/include/spawn.h
new file mode 100644 (file)
index 0000000..8532bcf
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef ULIBC_SPAWN_H
+#define ULIBC_SPAWN_H
+
+#include <stdint.h>
+#include <signal.h>
+
+typedef struct {
+    int     __flags;
+    int     __pgrp;
+    sigset_t __sd;
+    sigset_t __ss;
+} posix_spawnattr_t;
+
+typedef struct {
+    int __allocated;
+    int __used;
+    void* __actions;
+} posix_spawn_file_actions_t;
+
+int posix_spawn(int* pid, const char* path,
+                const posix_spawn_file_actions_t* file_actions,
+                const posix_spawnattr_t* attrp,
+                char* const argv[], char* const envp[]);
+
+int posix_spawnp(int* pid, const char* file,
+                 const posix_spawn_file_actions_t* file_actions,
+                 const posix_spawnattr_t* attrp,
+                 char* const argv[], char* const envp[]);
+
+int posix_spawn_file_actions_init(posix_spawn_file_actions_t* fact);
+int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* fact);
+int posix_spawnattr_init(posix_spawnattr_t* attr);
+int posix_spawnattr_destroy(posix_spawnattr_t* attr);
+
+#endif
index f39cb5eb26d008d5f6da596e5a43e24a896d8768..3cc79f55f89ede6c232bec0f78b47b601c67c555 100644 (file)
@@ -58,6 +58,8 @@ int     sprintf(char* buf, const char* fmt, ...) __attribute__((format(printf, 2
 int     snprintf(char* buf, size_t size, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
 int     vsnprintf(char* buf, size_t size, const char* fmt, va_list ap);
 int     sscanf(const char* str, const char* fmt, ...);
+int     scanf(const char* fmt, ...);
+int     fscanf(FILE* fp, const char* fmt, ...);
 int     remove(const char* path);
 int     rename(const char* oldpath, const char* newpath);
 
index d25ff1c47f6548f5a1989cad2af22f369ddb8772..4fb700bb9c0aedc4fd915780766a314e54885739 100644 (file)
@@ -29,6 +29,17 @@ void    srand(unsigned int seed);
 
 void    qsort(void* base, size_t nmemb, size_t size,
               int (*compar)(const void*, const void*));
+void*   bsearch(const void* key, const void* base, size_t nmemb, size_t size,
+                int (*compar)(const void*, const void*));
+
+typedef struct { int quot; int rem; } div_t;
+typedef struct { long quot; long rem; } ldiv_t;
+div_t   div(int numer, int denom);
+ldiv_t  ldiv(long numer, long denom);
+
+int     mkstemp(char* tmpl);
+double  strtod(const char* nptr, char** endptr);
+float   strtof(const char* nptr, char** endptr);
 
 int     system(const char* cmd);
 void    exit(int status) __attribute__((noreturn));
index 21023116efd5fe52d6b66dd857e380246021a57e..e4fd4f15aa3f6d3bdd2f85599305a0c005c93f40 100644 (file)
@@ -29,5 +29,6 @@ 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);
+char*   strsignal(int sig);
 
 #endif
diff --git a/user/ulibc/include/sys/epoll.h b/user/ulibc/include/sys/epoll.h
new file mode 100644 (file)
index 0000000..2c05c68
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef ULIBC_SYS_EPOLL_H
+#define ULIBC_SYS_EPOLL_H
+
+#include <stdint.h>
+
+#define EPOLL_CTL_ADD 1
+#define EPOLL_CTL_DEL 2
+#define EPOLL_CTL_MOD 3
+
+#define EPOLLIN      0x001
+#define EPOLLOUT     0x004
+#define EPOLLERR     0x008
+#define EPOLLHUP     0x010
+#define EPOLLRDHUP   0x2000
+#define EPOLLET      (1U << 31)
+#define EPOLLONESHOT (1 << 30)
+
+typedef union epoll_data {
+    void*    ptr;
+    int      fd;
+    uint32_t u32;
+    uint64_t u64;
+} epoll_data_t;
+
+struct epoll_event {
+    uint32_t     events;
+    epoll_data_t data;
+};
+
+int epoll_create(int size);
+int epoll_create1(int flags);
+int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
+int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
+
+#endif
diff --git a/user/ulibc/include/sys/inotify.h b/user/ulibc/include/sys/inotify.h
new file mode 100644 (file)
index 0000000..e61c8d1
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef ULIBC_SYS_INOTIFY_H
+#define ULIBC_SYS_INOTIFY_H
+
+#include <stdint.h>
+
+#define IN_ACCESS        0x00000001
+#define IN_MODIFY        0x00000002
+#define IN_ATTRIB        0x00000004
+#define IN_CLOSE_WRITE   0x00000008
+#define IN_CLOSE_NOWRITE 0x00000010
+#define IN_OPEN          0x00000020
+#define IN_MOVED_FROM    0x00000040
+#define IN_MOVED_TO      0x00000080
+#define IN_CREATE        0x00000100
+#define IN_DELETE         0x00000200
+#define IN_DELETE_SELF   0x00000400
+#define IN_MOVE_SELF     0x00000800
+#define IN_CLOSE         (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
+#define IN_MOVE          (IN_MOVED_FROM | IN_MOVED_TO)
+#define IN_ALL_EVENTS    0x00000FFF
+#define IN_NONBLOCK      0x00004000
+
+struct inotify_event {
+    int      wd;
+    uint32_t mask;
+    uint32_t cookie;
+    uint32_t len;
+    char     name[];
+};
+
+int inotify_init(void);
+int inotify_init1(int flags);
+int inotify_add_watch(int fd, const char* pathname, uint32_t mask);
+int inotify_rm_watch(int fd, int wd);
+
+#endif
diff --git a/user/ulibc/include/sys/shm.h b/user/ulibc/include/sys/shm.h
new file mode 100644 (file)
index 0000000..03596d9
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef ULIBC_SYS_SHM_H
+#define ULIBC_SYS_SHM_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define IPC_CREAT  01000
+#define IPC_EXCL   02000
+#define IPC_NOWAIT 04000
+#define IPC_RMID   0
+#define IPC_SET    1
+#define IPC_STAT   2
+#define IPC_PRIVATE 0
+
+typedef int key_t;
+
+struct shmid_ds {
+    int    shm_segsz;
+    int    shm_nattch;
+    int    shm_cpid;
+    int    shm_lpid;
+    uint32_t shm_atime;
+    uint32_t shm_dtime;
+    uint32_t shm_ctime;
+};
+
+int   shmget(key_t key, size_t size, int shmflg);
+void* shmat(int shmid, const void* shmaddr, int shmflg);
+int   shmdt(const void* shmaddr);
+int   shmctl(int shmid, int cmd, struct shmid_ds* buf);
+
+#endif
diff --git a/user/ulibc/include/sys/socket.h b/user/ulibc/include/sys/socket.h
new file mode 100644 (file)
index 0000000..dcfc164
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef ULIBC_SYS_SOCKET_H
+#define ULIBC_SYS_SOCKET_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Address families */
+#define AF_UNSPEC   0
+#define AF_UNIX     1
+#define AF_LOCAL    AF_UNIX
+#define AF_INET     2
+#define AF_INET6    10
+
+/* Socket types */
+#define SOCK_STREAM    1
+#define SOCK_DGRAM     2
+#define SOCK_RAW       3
+#define SOCK_NONBLOCK  0x800
+#define SOCK_CLOEXEC   0x80000
+
+/* Protocol families (aliases) */
+#define PF_UNSPEC   AF_UNSPEC
+#define PF_UNIX     AF_UNIX
+#define PF_INET     AF_INET
+#define PF_INET6    AF_INET6
+
+/* Socket options levels */
+#define SOL_SOCKET  1
+
+/* Socket options */
+#define SO_REUSEADDR  2
+#define SO_ERROR      4
+#define SO_BROADCAST  6
+#define SO_SNDBUF     7
+#define SO_RCVBUF     8
+#define SO_KEEPALIVE  9
+#define SO_LINGER     13
+#define SO_RCVTIMEO   20
+#define SO_SNDTIMEO   21
+
+/* shutdown() how */
+#define SHUT_RD   0
+#define SHUT_WR   1
+#define SHUT_RDWR 2
+
+/* MSG flags */
+#define MSG_PEEK       0x02
+#define MSG_DONTWAIT   0x40
+#define MSG_NOSIGNAL   0x4000
+
+typedef unsigned int socklen_t;
+typedef unsigned short sa_family_t;
+
+struct sockaddr {
+    sa_family_t sa_family;
+    char        sa_data[14];
+};
+
+struct sockaddr_storage {
+    sa_family_t ss_family;
+    char        __ss_pad[126];
+};
+
+struct msghdr {
+    void*        msg_name;
+    socklen_t    msg_namelen;
+    struct iovec* msg_iov;
+    int          msg_iovlen;
+    void*        msg_control;
+    socklen_t    msg_controllen;
+    int          msg_flags;
+};
+
+struct iovec;
+
+int socket(int domain, int type, int protocol);
+int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
+int listen(int sockfd, int backlog);
+int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
+int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
+int send(int sockfd, const void* buf, size_t len, int flags);
+int recv(int sockfd, void* buf, size_t len, int flags);
+int sendto(int sockfd, const void* buf, size_t len, int flags,
+           const struct sockaddr* dest_addr, socklen_t addrlen);
+int recvfrom(int sockfd, void* buf, size_t len, int flags,
+             struct sockaddr* src_addr, socklen_t* addrlen);
+int sendmsg(int sockfd, const struct msghdr* msg, int flags);
+int recvmsg(int sockfd, struct msghdr* msg, int flags);
+int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen);
+int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen);
+int shutdown(int sockfd, int how);
+int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
+int getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
+
+#endif
diff --git a/user/ulibc/include/sys/un.h b/user/ulibc/include/sys/un.h
new file mode 100644 (file)
index 0000000..65a41c5
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef ULIBC_SYS_UN_H
+#define ULIBC_SYS_UN_H
+
+#include <sys/socket.h>
+
+#define UNIX_PATH_MAX 108
+
+struct sockaddr_un {
+    sa_family_t sun_family;
+    char        sun_path[UNIX_PATH_MAX];
+};
+
+#endif
index 5c517ebcdc50e3965e684279edb23aea44ae46bd..8bd6cba213b60e982fd708226ea73434974eb4d9 100644 (file)
@@ -80,6 +80,54 @@ enum {
     SYS_SETEGID = 91,
     SYS_SETITIMER = 92,
     SYS_GETITIMER = 93,
+    SYS_SHMGET = 46,
+    SYS_SHMAT  = 47,
+    SYS_SHMDT  = 48,
+    SYS_SHMCTL = 49,
+    SYS_LINK   = 54,
+    SYS_SYMLINK = 55,
+    SYS_READLINK = 56,
+    SYS_SOCKET    = 58,
+    SYS_BIND      = 59,
+    SYS_LISTEN    = 60,
+    SYS_ACCEPT    = 61,
+    SYS_CONNECT   = 62,
+    SYS_SEND      = 63,
+    SYS_RECV      = 64,
+    SYS_SENDTO    = 65,
+    SYS_RECVFROM  = 66,
+    SYS_WAITID    = 94,
+    SYS_SIGQUEUE  = 95,
+    SYS_POSIX_SPAWN = 96,
+    SYS_MQ_OPEN     = 97,
+    SYS_MQ_CLOSE    = 98,
+    SYS_MQ_SEND     = 99,
+    SYS_MQ_RECEIVE  = 100,
+    SYS_MQ_UNLINK   = 101,
+    SYS_SEM_OPEN    = 102,
+    SYS_SEM_CLOSE   = 103,
+    SYS_SEM_WAIT    = 104,
+    SYS_SEM_POST    = 105,
+    SYS_SEM_UNLINK  = 106,
+    SYS_SEM_GETVALUE = 107,
+    SYS_GETADDRINFO  = 108,
+    SYS_DLOPEN       = 109,
+    SYS_DLSYM        = 110,
+    SYS_DLCLOSE      = 111,
+    SYS_EPOLL_CREATE = 112,
+    SYS_EPOLL_CTL    = 113,
+    SYS_EPOLL_WAIT   = 114,
+    SYS_INOTIFY_INIT      = 115,
+    SYS_INOTIFY_ADD_WATCH = 116,
+    SYS_INOTIFY_RM_WATCH  = 117,
+    SYS_SENDMSG  = 118,
+    SYS_RECVMSG  = 119,
+    SYS_PIVOT_ROOT = 120,
+    SYS_AIO_READ    = 121,
+    SYS_AIO_WRITE   = 122,
+    SYS_AIO_ERROR   = 123,
+    SYS_AIO_RETURN  = 124,
+    SYS_AIO_SUSPEND = 125,
     SYS_MOUNT = 126,
     SYS_GETTIMEOFDAY = 127,
     SYS_MPROTECT = 128,
@@ -92,6 +140,8 @@ enum {
     SYS_GETSOCKNAME = 135,
     SYS_UNAME = 136,
     SYS_GETRUSAGE = 137,
+    SYS_UMOUNT2 = 138,
+    SYS_WAIT4 = 139,
 };
 
 /* Raw syscall wrappers — up to 5 args via INT 0x80 */
diff --git a/user/ulibc/include/syslog.h b/user/ulibc/include/syslog.h
new file mode 100644 (file)
index 0000000..7da9a4e
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef ULIBC_SYSLOG_H
+#define ULIBC_SYSLOG_H
+
+#define LOG_EMERG   0
+#define LOG_ALERT   1
+#define LOG_CRIT    2
+#define LOG_ERR     3
+#define LOG_WARNING 4
+#define LOG_NOTICE  5
+#define LOG_INFO    6
+#define LOG_DEBUG   7
+
+#define LOG_KERN     (0<<3)
+#define LOG_USER     (1<<3)
+#define LOG_DAEMON   (3<<3)
+#define LOG_LOCAL0   (16<<3)
+
+#define LOG_PID    0x01
+#define LOG_CONS   0x02
+#define LOG_NDELAY 0x08
+#define LOG_NOWAIT 0x10
+
+void openlog(const char* ident, int option, int facility);
+void syslog(int priority, const char* format, ...);
+void closelog(void);
+
+#endif
index 1d4c15707f9c50387b1ffc9891522cfde89e34c0..85bedb15eecda429e89e5521bcfd4ac8210099e3 100644 (file)
@@ -52,4 +52,32 @@ int tcsetattr(int fd, int actions, const struct termios* t);
 #define TCSADRAIN 1
 #define TCSAFLUSH 2
 
+typedef unsigned int speed_t;
+
+#define B0      0
+#define B9600   9600
+#define B19200  19200
+#define B38400  38400
+#define B115200 115200
+
+speed_t cfgetispeed(const struct termios* t);
+speed_t cfgetospeed(const struct termios* t);
+int     cfsetispeed(struct termios* t, speed_t speed);
+int     cfsetospeed(struct termios* t, speed_t speed);
+void    cfmakeraw(struct termios* t);
+int     tcdrain(int fd);
+int     tcflush(int fd, int queue_selector);
+int     tcflow(int fd, int action);
+int     tcsendbreak(int fd, int duration);
+int     tcgetpgrp(int fd);
+int     tcsetpgrp(int fd, int pgrp);
+
+#define TCIFLUSH  0
+#define TCOFLUSH  1
+#define TCIOFLUSH 2
+#define TCOOFF    0
+#define TCOON     1
+#define TCIOFF    2
+#define TCION     3
+
 #endif
index faf3cb380033cb732f264a6d20748feb3bda7438..57ca07fc12167c0024ff29c6512274c760777ed1 100644 (file)
@@ -101,6 +101,15 @@ long fpathconf(int fd, int name);
 int     gethostname(char* name, size_t len);
 char*   ttyname(int fd);
 int     pipe2(int fds[2], int flags);
+int     execle(const char* path, const char* arg, ...);
+char*   getlogin(void);
+int     getlogin_r(char* buf, size_t bufsize);
+int     tcgetpgrp(int fd);
+int     tcsetpgrp(int fd, int pgrp);
+
+#define _CS_PATH 0
+long    confstr(int name, char* buf, size_t len);
+void*   sbrk(int increment);
 
 /* Environment pointer (set by crt0) */
 extern char** __environ;
diff --git a/user/ulibc/include/wordexp.h b/user/ulibc/include/wordexp.h
new file mode 100644 (file)
index 0000000..d109eab
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef ULIBC_WORDEXP_H
+#define ULIBC_WORDEXP_H
+
+#include <stddef.h>
+
+#define WRDE_DOOFFS  0x01
+#define WRDE_APPEND  0x02
+#define WRDE_NOCMD   0x04
+#define WRDE_REUSE   0x08
+#define WRDE_SHOWERR 0x10
+#define WRDE_UNDEF   0x20
+
+#define WRDE_BADCHAR 1
+#define WRDE_BADVAL  2
+#define WRDE_CMDSUB  3
+#define WRDE_NOSPACE 4
+#define WRDE_SYNTAX  5
+
+typedef struct {
+    size_t we_wordc;
+    char** we_wordv;
+    size_t we_offs;
+} wordexp_t;
+
+int  wordexp(const char* words, wordexp_t* pwordexp, int flags);
+void wordfree(wordexp_t* pwordexp);
+
+#endif
diff --git a/user/ulibc/src/aio.c b/user/ulibc/src/aio.c
new file mode 100644 (file)
index 0000000..364dbbd
--- /dev/null
@@ -0,0 +1,32 @@
+#include "aio.h"
+#include "syscall.h"
+#include "errno.h"
+
+int aio_read(struct aiocb* aiocbp) {
+    return __syscall_ret(_syscall3(SYS_AIO_READ, aiocbp->aio_fildes,
+                                   (int)aiocbp->aio_buf, (int)aiocbp->aio_nbytes));
+}
+
+int aio_write(struct aiocb* aiocbp) {
+    return __syscall_ret(_syscall3(SYS_AIO_WRITE, aiocbp->aio_fildes,
+                                   (int)aiocbp->aio_buf, (int)aiocbp->aio_nbytes));
+}
+
+int aio_error(const struct aiocb* aiocbp) {
+    return _syscall1(SYS_AIO_ERROR, (int)aiocbp);
+}
+
+int aio_return(struct aiocb* aiocbp) {
+    return _syscall1(SYS_AIO_RETURN, (int)aiocbp);
+}
+
+int aio_suspend(const struct aiocb* const list[], int nent,
+                const struct timespec* timeout) {
+    return __syscall_ret(_syscall3(SYS_AIO_SUSPEND, (int)list, nent, (int)timeout));
+}
+
+int aio_cancel(int fd, struct aiocb* aiocbp) {
+    (void)fd;
+    (void)aiocbp;
+    return AIO_ALLDONE;
+}
diff --git a/user/ulibc/src/dlfcn.c b/user/ulibc/src/dlfcn.c
new file mode 100644 (file)
index 0000000..150ba56
--- /dev/null
@@ -0,0 +1,44 @@
+#include "dlfcn.h"
+#include "syscall.h"
+#include "errno.h"
+
+static char _dlerror_buf[64];
+static int  _dlerror_set;
+
+void* dlopen(const char* filename, int flags) {
+    int ret = _syscall2(SYS_DLOPEN, (int)filename, flags);
+    if (ret < 0 && ret > -4096) {
+        _dlerror_set = 1;
+        errno = -ret;
+        return (void*)0;
+    }
+    return (void*)ret;
+}
+
+void* dlsym(void* handle, const char* symbol) {
+    int ret = _syscall2(SYS_DLSYM, (int)handle, (int)symbol);
+    if (ret < 0 && ret > -4096) {
+        _dlerror_set = 1;
+        errno = -ret;
+        return (void*)0;
+    }
+    return (void*)ret;
+}
+
+int dlclose(void* handle) {
+    return __syscall_ret(_syscall1(SYS_DLCLOSE, (int)handle));
+}
+
+char* dlerror(void) {
+    if (_dlerror_set) {
+        _dlerror_set = 0;
+        /* minimal message */
+        _dlerror_buf[0] = 'd'; _dlerror_buf[1] = 'l';
+        _dlerror_buf[2] = ' '; _dlerror_buf[3] = 'e';
+        _dlerror_buf[4] = 'r'; _dlerror_buf[5] = 'r';
+        _dlerror_buf[6] = 'o'; _dlerror_buf[7] = 'r';
+        _dlerror_buf[8] = '\0';
+        return _dlerror_buf;
+    }
+    return (void*)0;
+}
diff --git a/user/ulibc/src/epoll.c b/user/ulibc/src/epoll.c
new file mode 100644 (file)
index 0000000..7a5d111
--- /dev/null
@@ -0,0 +1,20 @@
+#include "sys/epoll.h"
+#include "syscall.h"
+#include "errno.h"
+
+int epoll_create(int size) {
+    (void)size;
+    return __syscall_ret(_syscall1(SYS_EPOLL_CREATE, 0));
+}
+
+int epoll_create1(int flags) {
+    return __syscall_ret(_syscall1(SYS_EPOLL_CREATE, flags));
+}
+
+int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event) {
+    return __syscall_ret(_syscall4(SYS_EPOLL_CTL, epfd, op, fd, (int)event));
+}
+
+int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout) {
+    return __syscall_ret(_syscall4(SYS_EPOLL_WAIT, epfd, (int)events, maxevents, timeout));
+}
diff --git a/user/ulibc/src/glob.c b/user/ulibc/src/glob.c
new file mode 100644 (file)
index 0000000..b83507a
--- /dev/null
@@ -0,0 +1,110 @@
+#include "glob.h"
+#include "fnmatch.h"
+#include "string.h"
+#include "stdlib.h"
+#include "unistd.h"
+#include "dirent.h"
+#include "errno.h"
+
+static int glob_add(glob_t* g, const char* path) {
+    if (g->gl_pathc + 1 >= g->__alloc) {
+        size_t newalloc = g->__alloc ? g->__alloc * 2 : 16;
+        char** nv = (char**)realloc(g->gl_pathv, newalloc * sizeof(char*));
+        if (!nv) return GLOB_NOSPACE;
+        g->gl_pathv = nv;
+        g->__alloc = newalloc;
+    }
+    g->gl_pathv[g->gl_pathc] = strdup(path);
+    if (!g->gl_pathv[g->gl_pathc]) return GLOB_NOSPACE;
+    g->gl_pathc++;
+    g->gl_pathv[g->gl_pathc] = (void*)0;
+    return 0;
+}
+
+int glob(const char* pattern, int flags,
+         int (*errfunc)(const char*, int), glob_t* pglob) {
+    (void)errfunc;
+
+    if (!(flags & GLOB_APPEND)) {
+        pglob->gl_pathc = 0;
+        pglob->gl_pathv = (void*)0;
+        pglob->__alloc = 0;
+    }
+
+    /* Split pattern into directory + basename */
+    char dir[256], base[256];
+    const char* last_slash = strrchr(pattern, '/');
+    if (last_slash) {
+        size_t dlen = (size_t)(last_slash - pattern);
+        if (dlen == 0) dlen = 1;
+        if (dlen >= sizeof(dir)) dlen = sizeof(dir) - 1;
+        memcpy(dir, pattern, dlen);
+        dir[dlen] = '\0';
+        strncpy(base, last_slash + 1, sizeof(base) - 1);
+        base[sizeof(base) - 1] = '\0';
+    } else {
+        strcpy(dir, ".");
+        strncpy(base, pattern, sizeof(base) - 1);
+        base[sizeof(base) - 1] = '\0';
+    }
+
+    /* Open directory and match entries */
+    int dfd = open(dir, 0);
+    if (dfd < 0) {
+        if (flags & GLOB_NOCHECK)
+            return glob_add(pglob, pattern);
+        return GLOB_ABORTED;
+    }
+
+    struct dirent de;
+    char path[512];
+    int found = 0;
+
+    while (getdents(dfd, &de, sizeof(de)) > 0) {
+        if (de.d_name[0] == '\0') continue;
+        if (fnmatch(base, de.d_name, 0) == 0) {
+            if (last_slash) {
+                snprintf(path, sizeof(path), "%s/%s", dir, de.d_name);
+            } else {
+                strncpy(path, de.d_name, sizeof(path) - 1);
+                path[sizeof(path) - 1] = '\0';
+            }
+            int rc = glob_add(pglob, path);
+            if (rc) { close(dfd); return rc; }
+            found++;
+        }
+    }
+
+    close(dfd);
+
+    if (found == 0) {
+        if (flags & GLOB_NOCHECK)
+            return glob_add(pglob, pattern);
+        return GLOB_NOMATCH;
+    }
+
+    if (!(flags & GLOB_NOSORT) && pglob->gl_pathc > 1) {
+        /* Simple insertion sort */
+        for (size_t i = 1; i < pglob->gl_pathc; i++) {
+            char* tmp = pglob->gl_pathv[i];
+            size_t j = i;
+            while (j > 0 && strcmp(pglob->gl_pathv[j - 1], tmp) > 0) {
+                pglob->gl_pathv[j] = pglob->gl_pathv[j - 1];
+                j--;
+            }
+            pglob->gl_pathv[j] = tmp;
+        }
+    }
+
+    return 0;
+}
+
+void globfree(glob_t* pglob) {
+    if (!pglob || !pglob->gl_pathv) return;
+    for (size_t i = 0; i < pglob->gl_pathc; i++)
+        free(pglob->gl_pathv[i]);
+    free(pglob->gl_pathv);
+    pglob->gl_pathv = (void*)0;
+    pglob->gl_pathc = 0;
+    pglob->__alloc = 0;
+}
diff --git a/user/ulibc/src/inet.c b/user/ulibc/src/inet.c
new file mode 100644 (file)
index 0000000..2a9f9d4
--- /dev/null
@@ -0,0 +1,73 @@
+#include "arpa/inet.h"
+#include "string.h"
+#include "errno.h"
+
+in_addr_t inet_addr(const char* cp) {
+    struct in_addr addr;
+    if (inet_aton(cp, &addr))
+        return addr.s_addr;
+    return INADDR_NONE;
+}
+
+int inet_aton(const char* cp, struct in_addr* inp) {
+    uint32_t val = 0;
+    int parts = 0;
+    uint32_t part = 0;
+    for (const char* p = cp; ; p++) {
+        if (*p >= '0' && *p <= '9') {
+            part = part * 10 + (unsigned)(*p - '0');
+            if (part > 255) return 0;
+        } else if (*p == '.' || *p == '\0') {
+            val = (val << 8) | part;
+            part = 0;
+            parts++;
+            if (*p == '\0') break;
+            if (parts >= 4) return 0;
+        } else {
+            return 0;
+        }
+    }
+    if (parts != 4) return 0;
+    if (inp) inp->s_addr = htonl(val);
+    return 1;
+}
+
+static char _ntoa_buf[16];
+char* inet_ntoa(struct in_addr in) {
+    uint32_t addr = ntohl(in.s_addr);
+    int pos = 0;
+    for (int i = 3; i >= 0; i--) {
+        unsigned int octet = (addr >> (i * 8)) & 0xFF;
+        if (octet >= 100) _ntoa_buf[pos++] = '0' + (char)(octet / 100);
+        if (octet >= 10) _ntoa_buf[pos++] = '0' + (char)((octet / 10) % 10);
+        _ntoa_buf[pos++] = '0' + (char)(octet % 10);
+        if (i > 0) _ntoa_buf[pos++] = '.';
+    }
+    _ntoa_buf[pos] = '\0';
+    return _ntoa_buf;
+}
+
+const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
+    if (af == AF_INET) {
+        struct in_addr a;
+        memcpy(&a, src, sizeof(a));
+        char* s = inet_ntoa(a);
+        size_t len = strlen(s);
+        if (len >= size) { errno = ENOSPC; return (void*)0; }
+        memcpy(dst, s, len + 1);
+        return dst;
+    }
+    errno = EAFNOSUPPORT;
+    return (void*)0;
+}
+
+int inet_pton(int af, const char* src, void* dst) {
+    if (af == AF_INET) {
+        struct in_addr addr;
+        if (!inet_aton(src, &addr)) return 0;
+        memcpy(dst, &addr, sizeof(addr));
+        return 1;
+    }
+    errno = EAFNOSUPPORT;
+    return -1;
+}
diff --git a/user/ulibc/src/inotify.c b/user/ulibc/src/inotify.c
new file mode 100644 (file)
index 0000000..f08264d
--- /dev/null
@@ -0,0 +1,19 @@
+#include "sys/inotify.h"
+#include "syscall.h"
+#include "errno.h"
+
+int inotify_init(void) {
+    return __syscall_ret(_syscall0(SYS_INOTIFY_INIT));
+}
+
+int inotify_init1(int flags) {
+    return __syscall_ret(_syscall1(SYS_INOTIFY_INIT, flags));
+}
+
+int inotify_add_watch(int fd, const char* pathname, uint32_t mask) {
+    return __syscall_ret(_syscall3(SYS_INOTIFY_ADD_WATCH, fd, (int)pathname, (int)mask));
+}
+
+int inotify_rm_watch(int fd, int wd) {
+    return __syscall_ret(_syscall2(SYS_INOTIFY_RM_WATCH, fd, wd));
+}
diff --git a/user/ulibc/src/mqueue.c b/user/ulibc/src/mqueue.c
new file mode 100644 (file)
index 0000000..f75c012
--- /dev/null
@@ -0,0 +1,40 @@
+#include "mqueue.h"
+#include "syscall.h"
+#include "errno.h"
+
+mqd_t mq_open(const char* name, int oflag, ...) {
+    return (mqd_t)__syscall_ret(_syscall2(SYS_MQ_OPEN, (int)name, oflag));
+}
+
+int mq_close(mqd_t mqdes) {
+    return __syscall_ret(_syscall1(SYS_MQ_CLOSE, mqdes));
+}
+
+int mq_send(mqd_t mqdes, const char* msg_ptr, size_t msg_len, unsigned int msg_prio) {
+    (void)msg_prio;
+    return __syscall_ret(_syscall3(SYS_MQ_SEND, mqdes, (int)msg_ptr, (int)msg_len));
+}
+
+int mq_receive(mqd_t mqdes, char* msg_ptr, size_t msg_len, unsigned int* msg_prio) {
+    (void)msg_prio;
+    return __syscall_ret(_syscall3(SYS_MQ_RECEIVE, mqdes, (int)msg_ptr, (int)msg_len));
+}
+
+int mq_unlink(const char* name) {
+    return __syscall_ret(_syscall1(SYS_MQ_UNLINK, (int)name));
+}
+
+int mq_getattr(mqd_t mqdes, struct mq_attr* attr) {
+    (void)mqdes;
+    (void)attr;
+    errno = ENOSYS;
+    return -1;
+}
+
+int mq_setattr(mqd_t mqdes, const struct mq_attr* newattr, struct mq_attr* oldattr) {
+    (void)mqdes;
+    (void)newattr;
+    (void)oldattr;
+    errno = ENOSYS;
+    return -1;
+}
diff --git a/user/ulibc/src/netdb.c b/user/ulibc/src/netdb.c
new file mode 100644 (file)
index 0000000..8736a56
--- /dev/null
@@ -0,0 +1,57 @@
+#include "netdb.h"
+#include "syscall.h"
+#include "errno.h"
+#include "stdlib.h"
+#include "string.h"
+
+int getaddrinfo(const char* node, const char* service,
+                const struct addrinfo* hints, struct addrinfo** res) {
+    (void)service;
+    if (!res) return EAI_FAIL;
+
+    /* Use kernel getaddrinfo syscall for DNS resolution */
+    struct sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+
+    int ret = _syscall3(SYS_GETADDRINFO, (int)node, (int)&addr.sin_addr, 0);
+    if (ret < 0) return EAI_NONAME;
+
+    struct addrinfo* ai = (struct addrinfo*)calloc(1, sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+    if (!ai) return EAI_MEMORY;
+
+    struct sockaddr_in* sa = (struct sockaddr_in*)(ai + 1);
+    memcpy(sa, &addr, sizeof(struct sockaddr_in));
+
+    ai->ai_family = AF_INET;
+    ai->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
+    ai->ai_protocol = hints ? hints->ai_protocol : 0;
+    ai->ai_addrlen = sizeof(struct sockaddr_in);
+    ai->ai_addr = (struct sockaddr*)sa;
+    ai->ai_next = (void*)0;
+    *res = ai;
+    return 0;
+}
+
+void freeaddrinfo(struct addrinfo* res) {
+    while (res) {
+        struct addrinfo* next = res->ai_next;
+        free(res);
+        res = next;
+    }
+}
+
+const char* gai_strerror(int errcode) {
+    switch (errcode) {
+    case 0: return "Success";
+    case EAI_NONAME: return "Name does not resolve";
+    case EAI_AGAIN: return "Temporary failure in name resolution";
+    case EAI_FAIL: return "Non-recoverable failure in name resolution";
+    case EAI_MEMORY: return "Memory allocation failure";
+    case EAI_FAMILY: return "Address family not supported";
+    case EAI_SOCKTYPE: return "Socket type not supported";
+    case EAI_SERVICE: return "Service not supported";
+    case EAI_SYSTEM: return "System error";
+    default: return "Unknown error";
+    }
+}
index df27ea56488efe9b70b4263e52563ec4e44ee42e..5146ef1509ace856c232f05c64149b76fe7a4172 100644 (file)
@@ -165,3 +165,325 @@ int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize) {
     attr->stack_size = stacksize;
     return 0;
 }
+
+int pthread_detach(pthread_t thread) {
+    (void)thread;
+    return 0;
+}
+
+int pthread_cancel(pthread_t thread) {
+    (void)thread;
+    return 0;
+}
+
+int pthread_setcancelstate(int state, int* oldstate) {
+    if (oldstate) *oldstate = 0;
+    (void)state;
+    return 0;
+}
+
+int pthread_setcanceltype(int type, int* oldtype) {
+    if (oldtype) *oldtype = 0;
+    (void)type;
+    return 0;
+}
+
+void pthread_testcancel(void) {}
+
+/* ---- Futex helpers ---- */
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+
+static int futex_wait(volatile int* addr, int val) {
+    return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAIT, val);
+}
+
+static int futex_wake(volatile int* addr, int count) {
+    return _syscall3(SYS_FUTEX, (int)addr, FUTEX_WAKE, count);
+}
+
+static int atomic_cas(volatile int* ptr, int old, int new_val) {
+    int prev;
+    __asm__ volatile("lock cmpxchgl %2, %1"
+                     : "=a"(prev), "+m"(*ptr)
+                     : "r"(new_val), "0"(old)
+                     : "memory");
+    return prev;
+}
+
+static int atomic_xchg(volatile int* ptr, int val) {
+    __asm__ volatile("xchgl %0, %1"
+                     : "=r"(val), "+m"(*ptr)
+                     : "0"(val)
+                     : "memory");
+    return val;
+}
+
+static void atomic_add(volatile int* ptr, int val) {
+    __asm__ volatile("lock addl %1, %0" : "+m"(*ptr) : "r"(val) : "memory");
+}
+
+/* ---- Mutex ---- */
+int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) {
+    if (!mutex) return 22;
+    mutex->__lock = 0;
+    mutex->__owner = 0;
+    mutex->__type = attr ? attr->__type : PTHREAD_MUTEX_NORMAL;
+    mutex->__count = 0;
+    return 0;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t* mutex) {
+    (void)mutex;
+    return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t* mutex) {
+    if (!mutex) return 22;
+    int tid = _syscall0(SYS_GETTID);
+
+    if (mutex->__type == PTHREAD_MUTEX_RECURSIVE && mutex->__owner == tid) {
+        mutex->__count++;
+        return 0;
+    }
+
+    while (atomic_xchg(&mutex->__lock, 1) != 0) {
+        futex_wait(&mutex->__lock, 1);
+    }
+    mutex->__owner = tid;
+    mutex->__count = 1;
+    return 0;
+}
+
+int pthread_mutex_trylock(pthread_mutex_t* mutex) {
+    if (!mutex) return 22;
+    int tid = _syscall0(SYS_GETTID);
+
+    if (mutex->__type == PTHREAD_MUTEX_RECURSIVE && mutex->__owner == tid) {
+        mutex->__count++;
+        return 0;
+    }
+
+    if (atomic_cas(&mutex->__lock, 0, 1) == 0) {
+        mutex->__owner = tid;
+        mutex->__count = 1;
+        return 0;
+    }
+    return 16; /* EBUSY */
+}
+
+int pthread_mutex_unlock(pthread_mutex_t* mutex) {
+    if (!mutex) return 22;
+
+    if (mutex->__type == PTHREAD_MUTEX_RECURSIVE) {
+        if (--mutex->__count > 0) return 0;
+    }
+
+    mutex->__owner = 0;
+    mutex->__count = 0;
+    atomic_xchg(&mutex->__lock, 0);
+    futex_wake(&mutex->__lock, 1);
+    return 0;
+}
+
+int pthread_mutexattr_init(pthread_mutexattr_t* attr) {
+    if (!attr) return 22;
+    attr->__type = PTHREAD_MUTEX_NORMAL;
+    return 0;
+}
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) {
+    (void)attr;
+    return 0;
+}
+
+int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) {
+    if (!attr) return 22;
+    attr->__type = type;
+    return 0;
+}
+
+int pthread_mutexattr_gettype(const pthread_mutexattr_t* attr, int* type) {
+    if (!attr || !type) return 22;
+    *type = attr->__type;
+    return 0;
+}
+
+/* ---- Condition Variable ---- */
+int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr) {
+    (void)attr;
+    if (!cond) return 22;
+    cond->__seq = 0;
+    return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t* cond) {
+    (void)cond;
+    return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
+    int seq = cond->__seq;
+    pthread_mutex_unlock(mutex);
+    futex_wait(&cond->__seq, seq);
+    pthread_mutex_lock(mutex);
+    return 0;
+}
+
+int pthread_cond_signal(pthread_cond_t* cond) {
+    atomic_add(&cond->__seq, 1);
+    futex_wake(&cond->__seq, 1);
+    return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t* cond) {
+    atomic_add(&cond->__seq, 1);
+    futex_wake(&cond->__seq, 0x7FFFFFFF);
+    return 0;
+}
+
+/* ---- Read-Write Lock ---- */
+int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t* attr) {
+    (void)attr;
+    if (!rwlock) return 22;
+    rwlock->__readers = 0;
+    rwlock->__writer = 0;
+    return 0;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) {
+    (void)rwlock;
+    return 0;
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) {
+    while (1) {
+        while (rwlock->__writer)
+            futex_wait(&rwlock->__writer, 1);
+        atomic_add(&rwlock->__readers, 1);
+        if (!rwlock->__writer) return 0;
+        atomic_add(&rwlock->__readers, -1);
+    }
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) {
+    while (atomic_xchg(&rwlock->__writer, 1) != 0)
+        futex_wait(&rwlock->__writer, 1);
+    while (rwlock->__readers > 0)
+        futex_wait(&rwlock->__readers, rwlock->__readers);
+    return 0;
+}
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) {
+    if (rwlock->__writer) return 16; /* EBUSY */
+    atomic_add(&rwlock->__readers, 1);
+    if (rwlock->__writer) {
+        atomic_add(&rwlock->__readers, -1);
+        return 16;
+    }
+    return 0;
+}
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) {
+    if (atomic_cas(&rwlock->__writer, 0, 1) != 0) return 16;
+    if (rwlock->__readers > 0) {
+        rwlock->__writer = 0;
+        return 16;
+    }
+    return 0;
+}
+
+int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) {
+    if (rwlock->__writer) {
+        rwlock->__writer = 0;
+        futex_wake(&rwlock->__writer, 0x7FFFFFFF);
+    } else {
+        atomic_add(&rwlock->__readers, -1);
+        if (rwlock->__readers == 0)
+            futex_wake(&rwlock->__readers, 1);
+    }
+    return 0;
+}
+
+/* ---- Thread-Specific Data ---- */
+#define PTHREAD_KEYS_MAX 32
+static void* _tsd_values[PTHREAD_KEYS_MAX];
+static void (*_tsd_destructors[PTHREAD_KEYS_MAX])(void*);
+static int _tsd_used[PTHREAD_KEYS_MAX];
+static int _tsd_next_key = 0;
+
+int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
+    if (!key) return 22;
+    if (_tsd_next_key >= PTHREAD_KEYS_MAX) return 11; /* EAGAIN */
+    int k = _tsd_next_key++;
+    _tsd_used[k] = 1;
+    _tsd_destructors[k] = destructor;
+    _tsd_values[k] = (void*)0;
+    *key = (pthread_key_t)k;
+    return 0;
+}
+
+int pthread_key_delete(pthread_key_t key) {
+    if (key >= PTHREAD_KEYS_MAX || !_tsd_used[key]) return 22;
+    _tsd_used[key] = 0;
+    _tsd_destructors[key] = (void*)0;
+    _tsd_values[key] = (void*)0;
+    return 0;
+}
+
+void* pthread_getspecific(pthread_key_t key) {
+    if (key >= PTHREAD_KEYS_MAX) return (void*)0;
+    return _tsd_values[key];
+}
+
+int pthread_setspecific(pthread_key_t key, const void* value) {
+    if (key >= PTHREAD_KEYS_MAX) return 22;
+    _tsd_values[key] = (void*)(uintptr_t)value;
+    return 0;
+}
+
+/* ---- Once ---- */
+int pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) {
+    if (!once_control || !init_routine) return 22;
+    if (atomic_cas(once_control, 0, 1) == 0) {
+        init_routine();
+        *once_control = 2;
+        futex_wake(once_control, 0x7FFFFFFF);
+    } else {
+        while (*once_control == 1)
+            futex_wait(once_control, 1);
+    }
+    return 0;
+}
+
+/* ---- Barrier ---- */
+int pthread_barrier_init(pthread_barrier_t* barrier,
+                         const pthread_barrierattr_t* attr, unsigned count) {
+    (void)attr;
+    if (!barrier || count == 0) return 22;
+    barrier->__count = 0;
+    barrier->__total = (int)count;
+    barrier->__seq = 0;
+    return 0;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t* barrier) {
+    (void)barrier;
+    return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t* barrier) {
+    int seq = barrier->__seq;
+    int n;
+    atomic_add(&barrier->__count, 1);
+    n = barrier->__count;
+    if (n >= barrier->__total) {
+        barrier->__count = 0;
+        atomic_add(&barrier->__seq, 1);
+        futex_wake(&barrier->__seq, 0x7FFFFFFF);
+        return PTHREAD_BARRIER_SERIAL_THREAD;
+    }
+    while (barrier->__seq == seq)
+        futex_wait(&barrier->__seq, seq);
+    return 0;
+}
index 79f668d109b24eca2a9325932dd0a6cd9c2cfc01..ebad3f6a0af75a67c94acf023a8a12d6c041bb6e 100644 (file)
@@ -1,9 +1,12 @@
 #include "pwd.h"
 #include "grp.h"
+#include "stdio.h"
+#include "string.h"
+#include "stdlib.h"
 #include <stddef.h>
 
-/* Minimal /etc/passwd and /etc/group stubs.
- * AdrOS has a single-user model (uid=0 root). */
+/* /etc/passwd and /etc/group parsing with static fallback.
+ * Format: name:passwd:uid:gid:gecos:dir:shell */
 
 static struct passwd _root = {
     .pw_name   = "root",
@@ -15,27 +18,85 @@ static struct passwd _root = {
     .pw_shell  = "/bin/sh",
 };
 
+static struct passwd _parsed_pw;
+static char _pw_line[256];
+static FILE* _pw_fp = (void*)0;
 static int _pw_idx = 0;
 
+static struct passwd* parse_passwd_line(char* line) {
+    char* fields[7];
+    int n = 0;
+    char* p = line;
+    fields[n++] = p;
+    while (*p && n < 7) {
+        if (*p == ':') { *p = '\0'; fields[n++] = p + 1; }
+        p++;
+    }
+    if (n < 7) return (struct passwd*)0;
+    _parsed_pw.pw_name   = fields[0];
+    _parsed_pw.pw_passwd = fields[1];
+    _parsed_pw.pw_uid    = atoi(fields[2]);
+    _parsed_pw.pw_gid    = atoi(fields[3]);
+    _parsed_pw.pw_gecos  = fields[4];
+    _parsed_pw.pw_dir    = fields[5];
+    _parsed_pw.pw_shell  = fields[6];
+    /* Strip trailing newline */
+    size_t sl = strlen(_parsed_pw.pw_shell);
+    if (sl > 0 && _parsed_pw.pw_shell[sl - 1] == '\n')
+        _parsed_pw.pw_shell[sl - 1] = '\0';
+    return &_parsed_pw;
+}
+
 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;
+    FILE* fp = fopen("/etc/passwd", "r");
+    if (fp) {
+        while (fgets(_pw_line, (int)sizeof(_pw_line), fp)) {
+            struct passwd* pw = parse_passwd_line(_pw_line);
+            if (pw && strcmp(pw->pw_name, name) == 0) {
+                fclose(fp);
+                return pw;
+            }
+        }
+        fclose(fp);
+    }
+    if (strcmp(name, "root") == 0) return &_root;
     return (struct passwd*)0;
 }
 
 struct passwd* getpwuid(int uid) {
+    FILE* fp = fopen("/etc/passwd", "r");
+    if (fp) {
+        while (fgets(_pw_line, (int)sizeof(_pw_line), fp)) {
+            struct passwd* pw = parse_passwd_line(_pw_line);
+            if (pw && pw->pw_uid == uid) {
+                fclose(fp);
+                return pw;
+            }
+        }
+        fclose(fp);
+    }
     if (uid == 0) return &_root;
     return (struct passwd*)0;
 }
 
-void setpwent(void) { _pw_idx = 0; }
-void endpwent(void) { _pw_idx = 0; }
+void setpwent(void) {
+    if (_pw_fp) fclose(_pw_fp);
+    _pw_fp = fopen("/etc/passwd", "r");
+    _pw_idx = 0;
+}
+
+void endpwent(void) {
+    if (_pw_fp) { fclose(_pw_fp); _pw_fp = (void*)0; }
+    _pw_idx = 0;
+}
 
 struct passwd* getpwent(void) {
+    if (_pw_fp) {
+        if (fgets(_pw_line, (int)sizeof(_pw_line), _pw_fp))
+            return parse_passwd_line(_pw_line);
+        return (struct passwd*)0;
+    }
     if (_pw_idx == 0) { _pw_idx++; return &_root; }
     return (struct passwd*)0;
 }
@@ -49,26 +110,91 @@ static struct group _root_grp = {
     .gr_mem    = _root_members,
 };
 
+static struct group _parsed_gr;
+static char _gr_line[256];
+static char* _gr_members[16];
+static FILE* _gr_fp = (void*)0;
 static int _gr_idx = 0;
 
+static struct group* parse_group_line(char* line) {
+    /* Format: name:passwd:gid:member1,member2,... */
+    char* fields[4];
+    int n = 0;
+    char* p = line;
+    fields[n++] = p;
+    while (*p && n < 4) {
+        if (*p == ':') { *p = '\0'; fields[n++] = p + 1; }
+        p++;
+    }
+    if (n < 3) return (struct group*)0;
+    _parsed_gr.gr_name   = fields[0];
+    _parsed_gr.gr_passwd = (n >= 2) ? fields[1] : "x";
+    _parsed_gr.gr_gid    = atoi(fields[2]);
+    /* Parse members */
+    int mi = 0;
+    if (n >= 4 && fields[3][0] != '\0' && fields[3][0] != '\n') {
+        char* mp = fields[3];
+        _gr_members[mi++] = mp;
+        while (*mp && mi < 15) {
+            if (*mp == ',' || *mp == '\n') { *mp = '\0'; _gr_members[mi++] = mp + 1; }
+            mp++;
+        }
+    }
+    _gr_members[mi] = (char*)0;
+    _parsed_gr.gr_mem = _gr_members;
+    return &_parsed_gr;
+}
+
 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;
+    FILE* fp = fopen("/etc/group", "r");
+    if (fp) {
+        while (fgets(_gr_line, (int)sizeof(_gr_line), fp)) {
+            struct group* gr = parse_group_line(_gr_line);
+            if (gr && strcmp(gr->gr_name, name) == 0) {
+                fclose(fp);
+                return gr;
+            }
+        }
+        fclose(fp);
+    }
+    if (strcmp(name, "root") == 0) return &_root_grp;
     return (struct group*)0;
 }
 
 struct group* getgrgid(int gid) {
+    FILE* fp = fopen("/etc/group", "r");
+    if (fp) {
+        while (fgets(_gr_line, (int)sizeof(_gr_line), fp)) {
+            struct group* gr = parse_group_line(_gr_line);
+            if (gr && gr->gr_gid == gid) {
+                fclose(fp);
+                return gr;
+            }
+        }
+        fclose(fp);
+    }
     if (gid == 0) return &_root_grp;
     return (struct group*)0;
 }
 
-void setgrent(void) { _gr_idx = 0; }
-void endgrent(void) { _gr_idx = 0; }
+void setgrent(void) {
+    if (_gr_fp) fclose(_gr_fp);
+    _gr_fp = fopen("/etc/group", "r");
+    _gr_idx = 0;
+}
+
+void endgrent(void) {
+    if (_gr_fp) { fclose(_gr_fp); _gr_fp = (void*)0; }
+    _gr_idx = 0;
+}
 
 struct group* getgrent(void) {
+    if (_gr_fp) {
+        if (fgets(_gr_line, (int)sizeof(_gr_line), _gr_fp))
+            return parse_group_line(_gr_line);
+        return (struct group*)0;
+    }
     if (_gr_idx == 0) { _gr_idx++; return &_root_grp; }
     return (struct group*)0;
 }
diff --git a/user/ulibc/src/semaphore.c b/user/ulibc/src/semaphore.c
new file mode 100644 (file)
index 0000000..bacbb9c
--- /dev/null
@@ -0,0 +1,47 @@
+#include "semaphore.h"
+#include "syscall.h"
+#include "errno.h"
+
+sem_t* sem_open(const char* name, int oflag, ...) {
+    int ret = _syscall2(SYS_SEM_OPEN, (int)name, oflag);
+    if (ret < 0 && ret > -4096) {
+        errno = -ret;
+        return SEM_FAILED;
+    }
+    return (sem_t*)(long)ret;
+}
+
+int sem_close(sem_t* sem) {
+    return __syscall_ret(_syscall1(SYS_SEM_CLOSE, (int)sem));
+}
+
+int sem_wait(sem_t* sem) {
+    return __syscall_ret(_syscall1(SYS_SEM_WAIT, (int)sem));
+}
+
+int sem_trywait(sem_t* sem) {
+    return __syscall_ret(_syscall1(SYS_SEM_WAIT, (int)sem));
+}
+
+int sem_post(sem_t* sem) {
+    return __syscall_ret(_syscall1(SYS_SEM_POST, (int)sem));
+}
+
+int sem_unlink(const char* name) {
+    return __syscall_ret(_syscall1(SYS_SEM_UNLINK, (int)name));
+}
+
+int sem_getvalue(sem_t* sem, int* sval) {
+    return __syscall_ret(_syscall2(SYS_SEM_GETVALUE, (int)sem, (int)sval));
+}
+
+int sem_init(sem_t* sem, int pshared, unsigned int value) {
+    (void)pshared;
+    if (sem) sem->__val = (int)value;
+    return 0;
+}
+
+int sem_destroy(sem_t* sem) {
+    (void)sem;
+    return 0;
+}
diff --git a/user/ulibc/src/shm.c b/user/ulibc/src/shm.c
new file mode 100644 (file)
index 0000000..f1b30a9
--- /dev/null
@@ -0,0 +1,24 @@
+#include "sys/shm.h"
+#include "syscall.h"
+#include "errno.h"
+
+int shmget(key_t key, size_t size, int shmflg) {
+    return __syscall_ret(_syscall3(SYS_SHMGET, key, (int)size, shmflg));
+}
+
+void* shmat(int shmid, const void* shmaddr, int shmflg) {
+    int ret = _syscall3(SYS_SHMAT, shmid, (int)shmaddr, shmflg);
+    if (ret < 0 && ret > -4096) {
+        errno = -ret;
+        return (void*)-1;
+    }
+    return (void*)ret;
+}
+
+int shmdt(const void* shmaddr) {
+    return __syscall_ret(_syscall1(SYS_SHMDT, (int)shmaddr));
+}
+
+int shmctl(int shmid, int cmd, struct shmid_ds* buf) {
+    return __syscall_ret(_syscall3(SYS_SHMCTL, shmid, cmd, (int)buf));
+}
diff --git a/user/ulibc/src/socket.c b/user/ulibc/src/socket.c
new file mode 100644 (file)
index 0000000..3ee60e3
--- /dev/null
@@ -0,0 +1,69 @@
+#include "sys/socket.h"
+#include "syscall.h"
+#include "errno.h"
+
+int socket(int domain, int type, int protocol) {
+    return __syscall_ret(_syscall3(SYS_SOCKET, domain, type, protocol));
+}
+
+int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen) {
+    return __syscall_ret(_syscall3(SYS_BIND, sockfd, (int)addr, (int)addrlen));
+}
+
+int listen(int sockfd, int backlog) {
+    return __syscall_ret(_syscall2(SYS_LISTEN, sockfd, backlog));
+}
+
+int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
+    return __syscall_ret(_syscall3(SYS_ACCEPT, sockfd, (int)addr, (int)addrlen));
+}
+
+int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen) {
+    return __syscall_ret(_syscall3(SYS_CONNECT, sockfd, (int)addr, (int)addrlen));
+}
+
+int send(int sockfd, const void* buf, size_t len, int flags) {
+    return __syscall_ret(_syscall4(SYS_SEND, sockfd, (int)buf, (int)len, flags));
+}
+
+int recv(int sockfd, void* buf, size_t len, int flags) {
+    return __syscall_ret(_syscall4(SYS_RECV, sockfd, (int)buf, (int)len, flags));
+}
+
+int sendto(int sockfd, const void* buf, size_t len, int flags,
+           const struct sockaddr* dest_addr, socklen_t addrlen) {
+    return __syscall_ret(_syscall5(SYS_SENDTO, sockfd, (int)buf, (int)len, flags, (int)dest_addr));
+}
+
+int recvfrom(int sockfd, void* buf, size_t len, int flags,
+             struct sockaddr* src_addr, socklen_t* addrlen) {
+    return __syscall_ret(_syscall5(SYS_RECVFROM, sockfd, (int)buf, (int)len, flags, (int)src_addr));
+}
+
+int sendmsg(int sockfd, const struct msghdr* msg, int flags) {
+    return __syscall_ret(_syscall3(SYS_SENDMSG, sockfd, (int)msg, flags));
+}
+
+int recvmsg(int sockfd, struct msghdr* msg, int flags) {
+    return __syscall_ret(_syscall3(SYS_RECVMSG, sockfd, (int)msg, flags));
+}
+
+int setsockopt(int sockfd, int level, int optname, const void* optval, socklen_t optlen) {
+    return __syscall_ret(_syscall5(SYS_SETSOCKOPT, sockfd, level, optname, (int)optval, (int)optlen));
+}
+
+int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen) {
+    return __syscall_ret(_syscall5(SYS_GETSOCKOPT, sockfd, level, optname, (int)optval, (int)optlen));
+}
+
+int shutdown(int sockfd, int how) {
+    return __syscall_ret(_syscall2(SYS_SHUTDOWN, sockfd, how));
+}
+
+int getpeername(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
+    return __syscall_ret(_syscall3(SYS_GETPEERNAME, sockfd, (int)addr, (int)addrlen));
+}
+
+int getsockname(int sockfd, struct sockaddr* addr, socklen_t* addrlen) {
+    return __syscall_ret(_syscall3(SYS_GETSOCKNAME, sockfd, (int)addr, (int)addrlen));
+}
diff --git a/user/ulibc/src/spawn.c b/user/ulibc/src/spawn.c
new file mode 100644 (file)
index 0000000..4cdc941
--- /dev/null
@@ -0,0 +1,47 @@
+#include "spawn.h"
+#include "syscall.h"
+#include "errno.h"
+#include "string.h"
+
+int posix_spawn(int* pid, const char* path,
+                const posix_spawn_file_actions_t* file_actions,
+                const posix_spawnattr_t* attrp,
+                char* const argv[], char* const envp[]) {
+    (void)file_actions;
+    (void)attrp;
+    (void)envp;
+    int ret = _syscall2(SYS_POSIX_SPAWN, (int)path, (int)argv);
+    if (ret < 0) {
+        errno = -ret;
+        return -ret;
+    }
+    if (pid) *pid = ret;
+    return 0;
+}
+
+int posix_spawnp(int* pid, const char* file,
+                 const posix_spawn_file_actions_t* file_actions,
+                 const posix_spawnattr_t* attrp,
+                 char* const argv[], char* const envp[]) {
+    return posix_spawn(pid, file, file_actions, attrp, argv, envp);
+}
+
+int posix_spawn_file_actions_init(posix_spawn_file_actions_t* fact) {
+    memset(fact, 0, sizeof(*fact));
+    return 0;
+}
+
+int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* fact) {
+    (void)fact;
+    return 0;
+}
+
+int posix_spawnattr_init(posix_spawnattr_t* attr) {
+    memset(attr, 0, sizeof(*attr));
+    return 0;
+}
+
+int posix_spawnattr_destroy(posix_spawnattr_t* attr) {
+    (void)attr;
+    return 0;
+}
index 5301c7a5f45de85a5393e40c8bbaf30b36630266..bee18231f62bac12ee63c45f1c40add54158859a 100644 (file)
@@ -576,3 +576,103 @@ char* tmpnam(char* s) {
     if (s) { strcpy(s, buf); return s; }
     return buf;
 }
+
+int fscanf(FILE* fp, const char* fmt, ...) {
+    /* Read a line, then delegate to sscanf */
+    char line[512];
+    if (!fgets(line, (int)sizeof(line), fp)) return EOF;
+    va_list ap;
+    va_start(ap, fmt);
+    /* We can't pass va_list to sscanf directly, so use a manual approach
+     * that matches sscanf's minimal implementation for %d and %s */
+    int count = 0;
+    const char* s = line;
+    const char* f = fmt;
+    while (*f && *s) {
+        if (*f == '%') {
+            f++;
+            if (*f == 'd' || *f == 'i') {
+                f++;
+                int* out = va_arg(ap, int*);
+                int neg = 0;
+                while (*s == ' ') s++;
+                if (*s == '-') { neg = 1; s++; }
+                else if (*s == '+') { s++; }
+                if (*s < '0' || *s > '9') break;
+                int val = 0;
+                while (*s >= '0' && *s <= '9') { val = val * 10 + (*s - '0'); s++; }
+                *out = neg ? -val : val;
+                count++;
+            } else if (*f == 's') {
+                f++;
+                char* out = va_arg(ap, char*);
+                while (*s == ' ') s++;
+                int i = 0;
+                while (*s && *s != ' ' && *s != '\n' && *s != '\t') out[i++] = *s++;
+                out[i] = '\0';
+                count++;
+            } else if (*f == 'c') {
+                f++;
+                char* out = va_arg(ap, char*);
+                *out = *s++;
+                count++;
+            } else {
+                break;
+            }
+        } else if (*f == ' ') {
+            f++;
+            while (*s == ' ') s++;
+        } else {
+            if (*f != *s) break;
+            f++; s++;
+        }
+    }
+    va_end(ap);
+    return count;
+}
+
+int scanf(const char* fmt, ...) {
+    char line[512];
+    if (!fgets(line, (int)sizeof(line), stdin)) return EOF;
+    va_list ap;
+    va_start(ap, fmt);
+    int count = 0;
+    const char* s = line;
+    const char* f = fmt;
+    while (*f && *s) {
+        if (*f == '%') {
+            f++;
+            if (*f == 'd' || *f == 'i') {
+                f++;
+                int* out = va_arg(ap, int*);
+                int neg = 0;
+                while (*s == ' ') s++;
+                if (*s == '-') { neg = 1; s++; }
+                else if (*s == '+') { s++; }
+                if (*s < '0' || *s > '9') break;
+                int val = 0;
+                while (*s >= '0' && *s <= '9') { val = val * 10 + (*s - '0'); s++; }
+                *out = neg ? -val : val;
+                count++;
+            } else if (*f == 's') {
+                f++;
+                char* out = va_arg(ap, char*);
+                while (*s == ' ') s++;
+                int i = 0;
+                while (*s && *s != ' ' && *s != '\n' && *s != '\t') out[i++] = *s++;
+                out[i] = '\0';
+                count++;
+            } else {
+                break;
+            }
+        } else if (*f == ' ') {
+            f++;
+            while (*s == ' ') s++;
+        } else {
+            if (*f != *s) break;
+            f++; s++;
+        }
+    }
+    va_end(ap);
+    return count;
+}
index 530d2fe662f4d35a05a482c32ca701f224fc6250..9d2b76235c7f5f8de08866dc487942a4ccb522d4 100644 (file)
@@ -291,3 +291,82 @@ int system(const char* cmd) {
     (void)cmd;
     return -1;
 }
+
+void* bsearch(const void* key, const void* base, size_t nmemb, size_t size,
+              int (*compar)(const void*, const void*)) {
+    const char* b = (const char*)base;
+    size_t lo = 0, hi = nmemb;
+    while (lo < hi) {
+        size_t mid = lo + (hi - lo) / 2;
+        int cmp = compar(key, b + mid * size);
+        if (cmp == 0) return (void*)(b + mid * size);
+        if (cmp < 0) hi = mid;
+        else lo = mid + 1;
+    }
+    return (void*)0;
+}
+
+div_t div(int numer, int denom) {
+    div_t r;
+    r.quot = numer / denom;
+    r.rem  = numer % denom;
+    return r;
+}
+
+ldiv_t ldiv(long numer, long denom) {
+    ldiv_t r;
+    r.quot = numer / denom;
+    r.rem  = numer % denom;
+    return r;
+}
+
+int mkstemp(char* tmpl) {
+    size_t len = strlen(tmpl);
+    if (len < 6) return -1;
+    char* suffix = tmpl + len - 6;
+    /* Check template ends with XXXXXX */
+    for (int i = 0; i < 6; i++)
+        if (suffix[i] != 'X') return -1;
+    /* Generate unique name using pid + counter */
+    extern int getpid(void);
+    static int counter = 0;
+    int id = getpid() * 1000 + (counter++ % 1000);
+    for (int i = 5; i >= 0; i--) {
+        suffix[i] = (char)('0' + id % 10);
+        id /= 10;
+    }
+    int fd = open(tmpl, 1 | 0x40 | 0x80 /* O_WRONLY|O_CREAT|O_EXCL */);
+    return fd;
+}
+
+double strtod(const char* nptr, char** endptr) {
+    const char* s = nptr;
+    int neg = 0;
+    while (*s == ' ' || *s == '\t') s++;
+    if (*s == '-') { neg = 1; s++; }
+    else if (*s == '+') { s++; }
+    double val = 0.0;
+    while (*s >= '0' && *s <= '9') { val = val * 10.0 + (*s - '0'); s++; }
+    if (*s == '.') {
+        s++;
+        double frac = 0.1;
+        while (*s >= '0' && *s <= '9') { val += (*s - '0') * frac; frac *= 0.1; s++; }
+    }
+    if (*s == 'e' || *s == 'E') {
+        s++;
+        int eneg = 0;
+        if (*s == '-') { eneg = 1; s++; }
+        else if (*s == '+') { s++; }
+        int exp = 0;
+        while (*s >= '0' && *s <= '9') { exp = exp * 10 + (*s - '0'); s++; }
+        double mult = 1.0;
+        for (int i = 0; i < exp; i++) mult *= 10.0;
+        if (eneg) val /= mult; else val *= mult;
+    }
+    if (endptr) *endptr = (char*)s;
+    return neg ? -val : val;
+}
+
+float strtof(const char* nptr, char** endptr) {
+    return (float)strtod(nptr, endptr);
+}
index 86920194397b38a4025ef89d08e12b4c24cef39f..45dd7d83500782847b5e9ee39951d31801563cf7 100644 (file)
@@ -190,3 +190,42 @@ char* strpbrk(const char* s, const char* accept) {
     }
     return (void*)0;
 }
+
+static char _sigbuf[24];
+char* strsignal(int sig) {
+    static const char* const names[] = {
+        [0]  = "Signal 0",
+        [1]  = "Hangup",
+        [2]  = "Interrupt",
+        [3]  = "Quit",
+        [4]  = "Illegal instruction",
+        [5]  = "Trace/breakpoint trap",
+        [6]  = "Aborted",
+        [7]  = "Bus error",
+        [8]  = "Floating point exception",
+        [9]  = "Killed",
+        [10] = "User defined signal 1",
+        [11] = "Segmentation fault",
+        [12] = "User defined signal 2",
+        [13] = "Broken pipe",
+        [14] = "Alarm clock",
+        [15] = "Terminated",
+        [17] = "Child exited",
+        [18] = "Continued",
+        [19] = "Stopped (signal)",
+        [20] = "Stopped",
+    };
+    if (sig >= 0 && sig <= 20 && names[sig])
+        return (char*)names[sig];
+    /* fallback: "Signal N" */
+    char* p = _sigbuf;
+    const char* prefix = "Signal ";
+    while (*prefix) *p++ = *prefix++;
+    if (sig < 0) { *p++ = '-'; sig = -sig; }
+    char digits[8];
+    int n = 0;
+    do { digits[n++] = (char)('0' + sig % 10); sig /= 10; } while (sig);
+    while (n > 0) *p++ = digits[--n];
+    *p = '\0';
+    return _sigbuf;
+}
diff --git a/user/ulibc/src/syslog.c b/user/ulibc/src/syslog.c
new file mode 100644 (file)
index 0000000..eb2b467
--- /dev/null
@@ -0,0 +1,22 @@
+#include "syslog.h"
+#include <stdarg.h>
+
+static const char* _syslog_ident = "";
+static int _syslog_facility = 0;
+
+void openlog(const char* ident, int option, int facility) {
+    (void)option;
+    _syslog_ident = ident ? ident : "";
+    _syslog_facility = facility;
+}
+
+void syslog(int priority, const char* format, ...) {
+    (void)priority;
+    (void)format;
+    /* Stub: AdrOS has no syslog daemon. Messages are silently dropped. */
+}
+
+void closelog(void) {
+    _syslog_ident = "";
+    _syslog_facility = 0;
+}
diff --git a/user/ulibc/src/termios.c b/user/ulibc/src/termios.c
new file mode 100644 (file)
index 0000000..64997a6
--- /dev/null
@@ -0,0 +1,62 @@
+#include "termios.h"
+#include "syscall.h"
+#include "errno.h"
+#include "string.h"
+
+speed_t cfgetispeed(const struct termios* t) {
+    (void)t;
+    return B115200;
+}
+
+speed_t cfgetospeed(const struct termios* t) {
+    (void)t;
+    return B115200;
+}
+
+int cfsetispeed(struct termios* t, speed_t speed) {
+    (void)t; (void)speed;
+    return 0;
+}
+
+int cfsetospeed(struct termios* t, speed_t speed) {
+    (void)t; (void)speed;
+    return 0;
+}
+
+void cfmakeraw(struct termios* t) {
+    t->c_iflag &= ~(ICRNL | IGNCR | INLCR);
+    t->c_oflag &= ~OPOST;
+    t->c_lflag &= ~(ECHO | ICANON | ISIG);
+    t->c_cc[VMIN] = 1;
+    t->c_cc[VTIME] = 0;
+}
+
+int tcdrain(int fd) {
+    return __syscall_ret(_syscall2(SYS_IOCTL, fd, TCSETSW));
+}
+
+int tcflush(int fd, int queue_selector) {
+    (void)queue_selector;
+    return __syscall_ret(_syscall2(SYS_IOCTL, fd, TCSETSF));
+}
+
+int tcflow(int fd, int action) {
+    (void)fd; (void)action;
+    return 0;
+}
+
+int tcsendbreak(int fd, int duration) {
+    (void)fd; (void)duration;
+    return 0;
+}
+
+int tcgetpgrp(int fd) {
+    int pgrp = 0;
+    int ret = _syscall3(SYS_IOCTL, fd, TIOCGPGRP, (int)&pgrp);
+    if (ret < 0) { errno = -ret; return -1; }
+    return pgrp;
+}
+
+int tcsetpgrp(int fd, int pgrp) {
+    return __syscall_ret(_syscall3(SYS_IOCTL, fd, TIOCSPGRP, (int)&pgrp));
+}
index f5b1755d045697b0ea7653a31d7853ec00f55f6b..e4c6d25b0e253dcb59a37042d3b6dc16cfd46a10 100644 (file)
@@ -201,20 +201,15 @@ int chown(const char* path, int owner, int group) {
 }
 
 int link(const char* oldpath, const char* newpath) {
-    /* Use SYS_UNLINKAT slot 38 — AdrOS doesn't have a dedicated link syscall yet,
-     * use a direct int $0x80 with the link syscall number if available */
-    (void)oldpath; (void)newpath;
-    return -1;  /* TODO: implement when kernel has SYS_LINK */
+    return __syscall_ret(_syscall2(SYS_LINK, (int)oldpath, (int)newpath));
 }
 
 int symlink(const char* target, const char* linkpath) {
-    (void)target; (void)linkpath;
-    return -1;  /* TODO: implement when kernel has SYS_SYMLINK */
+    return __syscall_ret(_syscall2(SYS_SYMLINK, (int)target, (int)linkpath));
 }
 
 int readlink(const char* path, char* buf, size_t bufsiz) {
-    (void)path; (void)buf; (void)bufsiz;
-    return -1;  /* TODO: implement when kernel has SYS_READLINK */
+    return __syscall_ret(_syscall3(SYS_READLINK, (int)path, (int)buf, (int)bufsiz));
 }
 
 int tcgetattr(int fd, struct termios* t) {
@@ -234,3 +229,60 @@ void _exit(int status) {
      * Cannot use hlt — it's privileged and causes #GP in ring 3. */
     for (;;) __asm__ volatile("nop");
 }
+
+int execle(const char* path, const char* arg, ...) {
+    /* Walk varargs to find argv[] and the trailing envp */
+    extern int execve(const char*, const char* const*, const char* const*);
+    const char* args[32];
+    int n = 0;
+    __builtin_va_list ap;
+    __builtin_va_start(ap, arg);
+    args[n++] = arg;
+    while (n < 31) {
+        const char* a = __builtin_va_arg(ap, const char*);
+        args[n++] = a;
+        if (!a) break;
+    }
+    args[n] = (void*)0;
+    char* const* envp = __builtin_va_arg(ap, char* const*);
+    __builtin_va_end(ap);
+    return execve(path, (const char* const*)args, (const char* const*)envp);
+}
+
+static char _login_buf[32] = "root";
+char* getlogin(void) {
+    return _login_buf;
+}
+
+int getlogin_r(char* buf, size_t bufsize) {
+    extern size_t strlen(const char*);
+    extern void*  memcpy(void*, const void*, size_t);
+    const char* name = getlogin();
+    size_t len = strlen(name);
+    if (len + 1 > bufsize) return 34; /* ERANGE */
+    memcpy(buf, name, len + 1);
+    return 0;
+}
+
+long confstr(int name, char* buf, size_t len) {
+    (void)name;
+    const char* val = "/bin:/usr/bin";
+    extern size_t strlen(const char*);
+    size_t vlen = strlen(val) + 1;
+    if (buf && len > 0) {
+        size_t copy = vlen < len ? vlen : len;
+        extern void* memcpy(void*, const void*, size_t);
+        memcpy(buf, val, copy);
+        if (copy < vlen && len > 0) buf[len - 1] = '\0';
+    }
+    return (long)vlen;
+}
+
+void* sbrk(int increment) {
+    void* cur = brk((void*)0);
+    if (increment == 0) return cur;
+    void* new_end = (void*)((char*)cur + increment);
+    void* result = brk(new_end);
+    if ((unsigned int)result < (unsigned int)new_end) return (void*)-1;
+    return cur;
+}
diff --git a/user/ulibc/src/wordexp.c b/user/ulibc/src/wordexp.c
new file mode 100644 (file)
index 0000000..c731999
--- /dev/null
@@ -0,0 +1,60 @@
+#include "wordexp.h"
+#include "string.h"
+#include "stdlib.h"
+
+int wordexp(const char* words, wordexp_t* pwordexp, int flags) {
+    if (!words || !pwordexp) return WRDE_BADCHAR;
+
+    if (!(flags & WRDE_APPEND)) {
+        pwordexp->we_wordc = 0;
+        pwordexp->we_wordv = (void*)0;
+    }
+
+    /* Minimal implementation: split on whitespace, no shell expansion */
+    char* copy = strdup(words);
+    if (!copy) return WRDE_NOSPACE;
+
+    size_t alloc = 8;
+    size_t offs = (flags & WRDE_DOOFFS) ? pwordexp->we_offs : 0;
+    char** wv = (char**)calloc(alloc + offs + 1, sizeof(char*));
+    if (!wv) { free(copy); return WRDE_NOSPACE; }
+
+    size_t count = 0;
+    char* saveptr;
+    char* tok = strtok_r(copy, " \t\n", &saveptr);
+    while (tok) {
+        if (count + offs + 1 >= alloc) {
+            alloc *= 2;
+            char** nv = (char**)realloc(wv, (alloc + offs + 1) * sizeof(char*));
+            if (!nv) {
+                for (size_t i = 0; i < count; i++) free(wv[offs + i]);
+                free(wv); free(copy);
+                return WRDE_NOSPACE;
+            }
+            wv = nv;
+        }
+        wv[offs + count] = strdup(tok);
+        if (!wv[offs + count]) {
+            for (size_t i = 0; i < count; i++) free(wv[offs + i]);
+            free(wv); free(copy);
+            return WRDE_NOSPACE;
+        }
+        count++;
+        tok = strtok_r((void*)0, " \t\n", &saveptr);
+    }
+    wv[offs + count] = (void*)0;
+
+    free(copy);
+    pwordexp->we_wordc = count;
+    pwordexp->we_wordv = wv;
+    return 0;
+}
+
+void wordfree(wordexp_t* pwordexp) {
+    if (!pwordexp || !pwordexp->we_wordv) return;
+    for (size_t i = 0; pwordexp->we_wordv[i]; i++)
+        free(pwordexp->we_wordv[i]);
+    free(pwordexp->we_wordv);
+    pwordexp->we_wordv = (void*)0;
+    pwordexp->we_wordc = 0;
+}