aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/posix
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/posix')
-rw-r--r--lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp220
-rw-r--r--lib/mlibc/options/posix/generic/dirent-stubs.cpp180
-rw-r--r--lib/mlibc/options/posix/generic/dlfcn-stubs.cpp64
-rw-r--r--lib/mlibc/options/posix/generic/fcntl-stubs.cpp108
-rw-r--r--lib/mlibc/options/posix/generic/ftw-stubs.cpp18
-rw-r--r--lib/mlibc/options/posix/generic/grp-stubs.cpp316
-rw-r--r--lib/mlibc/options/posix/generic/langinfo-stubs.cpp15
-rw-r--r--lib/mlibc/options/posix/generic/libgen-stubs.cpp51
-rw-r--r--lib/mlibc/options/posix/generic/lookup.cpp512
-rw-r--r--lib/mlibc/options/posix/generic/mqueue.cpp22
-rw-r--r--lib/mlibc/options/posix/generic/net-if-stubs.cpp40
-rw-r--r--lib/mlibc/options/posix/generic/netdb-stubs.cpp486
-rw-r--r--lib/mlibc/options/posix/generic/poll.cpp31
-rw-r--r--lib/mlibc/options/posix/generic/posix-file-io.cpp275
-rw-r--r--lib/mlibc/options/posix/generic/posix_ctype.cpp136
-rw-r--r--lib/mlibc/options/posix/generic/posix_locale.cpp37
-rw-r--r--lib/mlibc/options/posix/generic/posix_signal.cpp151
-rw-r--r--lib/mlibc/options/posix/generic/posix_stdio.cpp209
-rw-r--r--lib/mlibc/options/posix/generic/posix_stdlib.cpp513
-rw-r--r--lib/mlibc/options/posix/generic/posix_string.cpp174
-rw-r--r--lib/mlibc/options/posix/generic/posix_time.cpp32
-rw-r--r--lib/mlibc/options/posix/generic/pthread-stubs.cpp1426
-rw-r--r--lib/mlibc/options/posix/generic/pwd-stubs.cpp284
-rw-r--r--lib/mlibc/options/posix/generic/resolv_conf.cpp42
-rw-r--r--lib/mlibc/options/posix/generic/sched-stubs.cpp50
-rw-r--r--lib/mlibc/options/posix/generic/search.cpp151
-rw-r--r--lib/mlibc/options/posix/generic/semaphore-stubs.cpp114
-rw-r--r--lib/mlibc/options/posix/generic/services.cpp222
-rw-r--r--lib/mlibc/options/posix/generic/spawn-stubs.cpp376
-rw-r--r--lib/mlibc/options/posix/generic/strings-stubs.cpp107
-rw-r--r--lib/mlibc/options/posix/generic/sys-file-stubs.cpp16
-rw-r--r--lib/mlibc/options/posix/generic/sys-ipc.cpp8
-rw-r--r--lib/mlibc/options/posix/generic/sys-mman-stubs.cpp177
-rw-r--r--lib/mlibc/options/posix/generic/sys-msg.cpp23
-rw-r--r--lib/mlibc/options/posix/generic/sys-resource-stubs.cpp57
-rw-r--r--lib/mlibc/options/posix/generic/sys-select-stubs.cpp58
-rw-r--r--lib/mlibc/options/posix/generic/sys-sem.cpp51
-rw-r--r--lib/mlibc/options/posix/generic/sys-shm.cpp24
-rw-r--r--lib/mlibc/options/posix/generic/sys-socket-stubs.cpp225
-rw-r--r--lib/mlibc/options/posix/generic/sys-stat-stubs.cpp155
-rw-r--r--lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp24
-rw-r--r--lib/mlibc/options/posix/generic/sys-time-stubs.cpp107
-rw-r--r--lib/mlibc/options/posix/generic/sys-times.cpp19
-rw-r--r--lib/mlibc/options/posix/generic/sys-uio.cpp67
-rw-r--r--lib/mlibc/options/posix/generic/sys-utsname.cpp24
-rw-r--r--lib/mlibc/options/posix/generic/sys-wait-stubs.cpp52
-rw-r--r--lib/mlibc/options/posix/generic/syslog-stubs.cpp152
-rw-r--r--lib/mlibc/options/posix/generic/termios-stubs.cpp103
-rw-r--r--lib/mlibc/options/posix/generic/time.cpp505
-rw-r--r--lib/mlibc/options/posix/generic/ucontext-stubs.cpp19
-rw-r--r--lib/mlibc/options/posix/generic/unistd-stubs.cpp1227
-rw-r--r--lib/mlibc/options/posix/generic/utime-stubs.cpp31
-rw-r--r--lib/mlibc/options/posix/generic/wordexp-stubs.cpp342
-rw-r--r--lib/mlibc/options/posix/include/arpa/inet.h46
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/fd_set.h14
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/id_t.h6
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/in_addr_t.h8
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/in_port_t.h8
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/iovec.h11
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/locale_t.h14
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_ctype.h36
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_locale.h23
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_signal.h111
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_stdio.h72
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_stdlib.h73
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_string.h57
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_time.h25
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/posix_wctype.h44
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/pthread_t.h8
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/stat.h24
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/timer_t.h6
-rw-r--r--lib/mlibc/options/posix/include/bits/posix/timeval.h12
-rw-r--r--lib/mlibc/options/posix/include/byteswap.h23
-rw-r--r--lib/mlibc/options/posix/include/dirent.h76
-rw-r--r--lib/mlibc/options/posix/include/dlfcn.h52
-rw-r--r--lib/mlibc/options/posix/include/fcntl.h76
-rw-r--r--lib/mlibc/options/posix/include/fnmatch.h33
-rw-r--r--lib/mlibc/options/posix/include/ftw.h43
-rw-r--r--lib/mlibc/options/posix/include/glob.h58
-rw-r--r--lib/mlibc/options/posix/include/grp.h43
-rw-r--r--lib/mlibc/options/posix/include/langinfo.h24
-rw-r--r--lib/mlibc/options/posix/include/libgen.h28
-rw-r--r--lib/mlibc/options/posix/include/mlibc/lookup.hpp58
-rw-r--r--lib/mlibc/options/posix/include/mlibc/posix-file-io.hpp102
-rw-r--r--lib/mlibc/options/posix/include/mlibc/posix-sysdeps.hpp240
-rw-r--r--lib/mlibc/options/posix/include/mlibc/resolv_conf.hpp21
-rw-r--r--lib/mlibc/options/posix/include/mlibc/services.hpp33
-rw-r--r--lib/mlibc/options/posix/include/mqueue.h26
-rw-r--r--lib/mlibc/options/posix/include/net/if.h118
-rw-r--r--lib/mlibc/options/posix/include/net/if_arp.h103
-rw-r--r--lib/mlibc/options/posix/include/netdb.h148
-rw-r--r--lib/mlibc/options/posix/include/netinet/icmp6.h139
-rw-r--r--lib/mlibc/options/posix/include/netinet/if_ether.h105
-rw-r--r--lib/mlibc/options/posix/include/netinet/in.h118
-rw-r--r--lib/mlibc/options/posix/include/netinet/ip.h75
-rw-r--r--lib/mlibc/options/posix/include/netinet/ip6.h28
-rw-r--r--lib/mlibc/options/posix/include/netinet/ip_icmp.h84
-rw-r--r--lib/mlibc/options/posix/include/netinet/tcp.h37
-rw-r--r--lib/mlibc/options/posix/include/netinet/udp.h31
-rw-r--r--lib/mlibc/options/posix/include/nl_types.h6
-rw-r--r--lib/mlibc/options/posix/include/poll.h6
-rw-r--r--lib/mlibc/options/posix/include/pthread.h325
-rw-r--r--lib/mlibc/options/posix/include/pwd.h45
-rw-r--r--lib/mlibc/options/posix/include/regex.h66
-rw-r--r--lib/mlibc/options/posix/include/sched.h49
-rw-r--r--lib/mlibc/options/posix/include/search.h37
-rw-r--r--lib/mlibc/options/posix/include/semaphore.h37
-rw-r--r--lib/mlibc/options/posix/include/spawn.h82
-rw-r--r--lib/mlibc/options/posix/include/strings.h32
-rw-r--r--lib/mlibc/options/posix/include/sys/file.h25
-rw-r--r--lib/mlibc/options/posix/include/sys/ipc.h53
-rw-r--r--lib/mlibc/options/posix/include/sys/mman.h47
-rw-r--r--lib/mlibc/options/posix/include/sys/msg.h27
-rw-r--r--lib/mlibc/options/posix/include/sys/param.h36
-rw-r--r--lib/mlibc/options/posix/include/sys/poll.h37
-rw-r--r--lib/mlibc/options/posix/include/sys/resource.h52
-rw-r--r--lib/mlibc/options/posix/include/sys/select.h49
-rw-r--r--lib/mlibc/options/posix/include/sys/sem.h44
-rw-r--r--lib/mlibc/options/posix/include/sys/shm.h83
-rw-r--r--lib/mlibc/options/posix/include/sys/socket.h105
-rw-r--r--lib/mlibc/options/posix/include/sys/stat.h37
-rw-r--r--lib/mlibc/options/posix/include/sys/statvfs.h22
-rw-r--r--lib/mlibc/options/posix/include/sys/syslog.h1
-rw-r--r--lib/mlibc/options/posix/include/sys/termios.h6
-rw-r--r--lib/mlibc/options/posix/include/sys/time.h68
-rw-r--r--lib/mlibc/options/posix/include/sys/times.h28
-rw-r--r--lib/mlibc/options/posix/include/sys/ttydefaults.h39
-rw-r--r--lib/mlibc/options/posix/include/sys/types.h53
-rw-r--r--lib/mlibc/options/posix/include/sys/uio.h31
-rw-r--r--lib/mlibc/options/posix/include/sys/un.h24
-rw-r--r--lib/mlibc/options/posix/include/sys/utsname.h22
-rw-r--r--lib/mlibc/options/posix/include/sys/wait.h40
-rw-r--r--lib/mlibc/options/posix/include/syslog.h75
-rw-r--r--lib/mlibc/options/posix/include/termios.h100
-rw-r--r--lib/mlibc/options/posix/include/ucontext.h23
-rw-r--r--lib/mlibc/options/posix/include/unistd.h360
-rw-r--r--lib/mlibc/options/posix/include/utime.h25
-rw-r--r--lib/mlibc/options/posix/include/wordexp.h43
-rw-r--r--lib/mlibc/options/posix/meson.build175
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/fnmatch.c321
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/glob.c311
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/regcomp.c2953
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/regerror.c37
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/regexec.c1028
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/tre-mem.c158
-rw-r--r--lib/mlibc/options/posix/musl-generic-regex/tre.h241
146 files changed, 19812 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp b/lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp
new file mode 100644
index 0000000..0bc9727
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/arpa-inet-stubs.cpp
@@ -0,0 +1,220 @@
+
+#include <arpa/inet.h>
+#include <bits/ensure.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <mlibc/bitutil.hpp>
+#include <mlibc/debug.hpp>
+
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+
+uint32_t htonl(uint32_t x) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ return mlibc::bit_util<uint32_t>::byteswap(x);
+#else
+ return x;
+#endif
+}
+uint16_t htons(uint16_t x) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ return mlibc::bit_util<uint16_t>::byteswap(x);
+#else
+ return x;
+#endif
+}
+uint32_t ntohl(uint32_t x) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ return mlibc::bit_util<uint32_t>::byteswap(x);
+#else
+ return x;
+#endif
+}
+uint16_t ntohs(uint16_t x) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ return mlibc::bit_util<uint16_t>::byteswap(x);
+#else
+ return x;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// IPv4 address manipulation.
+// ----------------------------------------------------------------------------
+in_addr_t inet_addr(const char *p) {
+ struct in_addr a;
+ if(!inet_aton(p, &a))
+ return -1;
+ return a.s_addr;
+}
+char *inet_ntoa(struct in_addr addr) {
+ // string: xxx.yyy.zzz.aaa
+ // 4 * 3 + 3 + 1 = 12 + 4 = 16
+ thread_local static char buffer[16];
+ uint32_t proper = htonl(addr.s_addr);
+ snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d",
+ (proper >> 24) & 0xff, ((proper >> 16) & 0xff),
+ (proper >> 8) & 0xff, proper & 0xff);
+ return buffer;
+}
+int inet_aton(const char *string, struct in_addr *dest) {
+ int array[4];
+ int i = 0;
+ char *end;
+
+ for (; i < 4; i++) {
+ array[i] = strtoul(string, &end, 0);
+ if (*end && *end != '.')
+ return 0;
+ if (!*end)
+ break;
+ string = end + 1;
+ }
+
+ switch (i) {
+ case 0:
+ dest->s_addr = htonl(array[0]);
+ break;
+ case 1:
+ if (array[0] > 255 || array[1] > 0xffffff)
+ return 0;
+ dest->s_addr = htonl((array[0] << 24) | array[1]);
+ break;
+ case 2:
+ if (array[0] > 255 || array[1] > 255 ||
+ array[2] > 0xffff)
+ return 0;
+ dest->s_addr = htonl((array[0] << 24) | (array[1] << 16) |
+ array[2]);
+ break;
+ case 3:
+ if (array[0] > 255 || array[1] > 255 ||
+ array[2] > 255 || array[3] > 255)
+ return 0;
+ dest->s_addr = htonl((array[0] << 24) | (array[1] << 16) |
+ (array[2] << 8) | array[3]);
+ break;
+ }
+
+ return 1;
+}
+
+// ----------------------------------------------------------------------------
+// Generic IP address manipulation.
+// ----------------------------------------------------------------------------
+const char *inet_ntop(int af, const void *__restrict src, char *__restrict dst,
+ socklen_t size) {
+ switch (af) {
+ case AF_INET: {
+ auto source = reinterpret_cast<const struct in_addr*>(src);
+ if (snprintf(dst, size, "%d.%d.%d.%d",
+ source->s_addr & 0xff,
+ (source->s_addr & 0xffff) >> 8,
+ (source->s_addr & 0xffffff) >> 16,
+ source->s_addr >> 24) < (int)size)
+ return dst;
+ break;
+ }
+ case AF_INET6: {
+ auto source = reinterpret_cast<const struct in6_addr*>(src);
+ size_t cur_zeroes_off = 0;
+ size_t cur_zeroes_len = 0;
+ size_t max_zeroes_off = 0;
+ size_t max_zeroes_len = 0;
+
+ /* we look for the largest block of zeroed quartet(s) */
+ for(size_t i = 0; i < 8; i++) {
+ auto ptr = source->s6_addr + (i * 2);
+ if(!ptr[0] && !ptr[1]) {
+ cur_zeroes_len++;
+ if(max_zeroes_len < cur_zeroes_len) {
+ max_zeroes_len = cur_zeroes_len;
+ max_zeroes_off = cur_zeroes_off;
+ }
+ } else {
+ /* advance the offset to the next quartet to check */
+ cur_zeroes_len = 0;
+ cur_zeroes_off = i + 1;
+ }
+ }
+
+ size_t off = 0;
+ for(size_t i = 0; i < 8; i++) {
+ auto ptr = source->s6_addr + (i * 2);
+
+ /* if we are at the beginning of the largest block of zeroed quartets, place "::" */
+ if(i == max_zeroes_off && max_zeroes_len >= 2) {
+ if(off < size) {
+ dst[off++] = ':';
+ }
+ if(off < size) {
+ dst[off++] = ':';
+ }
+ i += max_zeroes_len - 1;
+
+ continue;
+ }
+
+ /* place a colon if we're not at the beginning of the string and it is not already there */
+ if(off && dst[off - 1] != ':') {
+ if(off < size) {
+ dst[off++] = ':';
+ }
+ }
+
+ off += snprintf(dst + off, size - off, "%x", ptr[0] << 8 | ptr[1]);
+ }
+
+ dst[off] = 0;
+
+ return dst;
+ }
+ default:
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+
+ errno = ENOSPC;
+ return NULL;
+}
+int inet_pton(int af, const char *__restrict src, void *__restrict dst) {
+ switch (af) {
+ case AF_INET: {
+ uint8_t array[4] = {};
+ for (int i = 0; i < 4; i++) {
+ char *end;
+ long int value = strtol(src, &end, 10);
+ if (value > 255)
+ return 0;
+ if (*end != '\0' && *end != '.')
+ return 0;
+ src = end + 1;
+ array[i] = value;
+ }
+ auto addr = reinterpret_cast<struct in_addr*>(dst);
+ memcpy(&addr->s_addr, array, 4);
+ break;
+ }
+ case AF_INET6:
+ mlibc::infoLogger() << "inet_pton: ipv6 is not implemented!" << frg::endlog;
+ /* fallthrough */
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ return 1;
+}
+
+struct in_addr inet_makeaddr(in_addr_t, in_addr_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+in_addr_t inet_netof(struct in_addr) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/dirent-stubs.cpp b/lib/mlibc/options/posix/generic/dirent-stubs.cpp
new file mode 100644
index 0000000..1352585
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/dirent-stubs.cpp
@@ -0,0 +1,180 @@
+
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#include <bits/ensure.h>
+#include <frg/allocation.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+
+// Code taken from musl
+int alphasort(const struct dirent **a, const struct dirent **b) {
+ return strcoll((*a)->d_name, (*b)->d_name);
+}
+
+int closedir(DIR *dir) {
+ // TODO: Deallocate the dir structure.
+ close(dir->__handle);
+ return 0;
+}
+int dirfd(DIR *dir) {
+ return dir->__handle;
+}
+DIR *fdopendir(int fd) {
+ struct stat st;
+
+ if(fstat(fd, &st) < 0) {
+ return nullptr;
+ }
+ // Musl implements this, but O_PATH is only declared on the linux abi
+ /*if(fcntl(fd, F_GETFL) & O_PATH) {
+ errno = EBADF;
+ return nullptr;
+ }*/
+ if(!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return nullptr;
+ }
+ auto dir = frg::construct<__mlibc_dir_struct>(getAllocator());
+ __ensure(dir);
+ dir->__ent_next = 0;
+ dir->__ent_limit = 0;
+ int flags = fcntl(fd, F_GETFD);
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ dir->__handle = fd;
+ return dir;
+}
+DIR *opendir(const char *path) {
+ auto dir = frg::construct<__mlibc_dir_struct>(getAllocator());
+ __ensure(dir);
+ dir->__ent_next = 0;
+ dir->__ent_limit = 0;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_open_dir, nullptr);
+ if(int e = mlibc::sys_open_dir(path, &dir->__handle); e) {
+ errno = e;
+ frg::destruct(getAllocator(), dir);
+ return nullptr;
+ }else{
+ return dir;
+ }
+}
+struct dirent *readdir(DIR *dir) {
+ __ensure(dir->__ent_next <= dir->__ent_limit);
+ if(dir->__ent_next == dir->__ent_limit) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_read_entries, nullptr);
+ if(int e = mlibc::sys_read_entries(dir->__handle, dir->__ent_buffer, 2048, &dir->__ent_limit); e)
+ __ensure(!"mlibc::sys_read_entries() failed");
+ dir->__ent_next = 0;
+ if(!dir->__ent_limit)
+ return nullptr;
+ }
+
+ auto entp = reinterpret_cast<struct dirent *>(dir->__ent_buffer + dir->__ent_next);
+ // We only copy as many bytes as we need to avoid buffer-overflows.
+ memcpy(&dir->__current, entp, offsetof(struct dirent, d_name) + strlen(entp->d_name) + 1);
+ dir->__ent_next += entp->d_reclen;
+ return &dir->__current;
+}
+int readdir_r(DIR *dir, struct dirent *entry, struct dirent **result) {
+ if(!mlibc::sys_read_entries) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+
+ __ensure(dir->__ent_next <= dir->__ent_limit);
+ if(dir->__ent_next == dir->__ent_limit) {
+ if(int e = mlibc::sys_read_entries(dir->__handle, dir->__ent_buffer, 2048, &dir->__ent_limit); e)
+ __ensure(!"mlibc::sys_read_entries() failed");
+ dir->__ent_next = 0;
+ if(!dir->__ent_limit) {
+ *result = NULL;
+ return 0;
+ }
+ }
+
+ auto entp = reinterpret_cast<struct dirent *>(dir->__ent_buffer + dir->__ent_next);
+ // We only copy as many bytes as we need to avoid buffer-overflows.
+ memcpy(entry, entp, offsetof(struct dirent, d_name) + strlen(entp->d_name) + 1);
+ dir->__ent_next += entp->d_reclen;
+ *result = entry;
+ return 0;
+}
+
+void rewinddir(DIR *dir) {
+ lseek(dir->__handle, 0, SEEK_SET);
+ dir->__ent_next = 0;
+}
+
+int scandir(const char *path, struct dirent ***res, int (*select)(const struct dirent *),
+ int (*compare)(const struct dirent **, const struct dirent **)) {
+ DIR *dir = opendir(path);
+ if (!dir)
+ return -1; // errno will be set by opendir()
+
+ // we should save the errno
+ int old_errno = errno;
+ errno = 0;
+
+ struct dirent *dir_ent;
+ struct dirent **array = nullptr, **tmp = nullptr;
+ int length = 0;
+ int count = 0;
+ while((dir_ent = readdir(dir)) && !errno) {
+ if(select && !select(dir_ent))
+ continue;
+
+ if(count >= length) {
+ length = 2*length + 1;
+ tmp = static_cast<struct dirent**>(realloc(array,
+ length * sizeof(struct dirent*)));
+ // we need to check the call actually goes through
+ // before we overwrite array so that we can
+ // deallocate the already written entries should realloc()
+ // have failed
+ if(!tmp)
+ break;
+ array = tmp;
+ }
+ array[count] = static_cast<struct dirent*>(malloc(dir_ent->d_reclen));
+ if(!array[count])
+ break;
+
+ memcpy(array[count], dir_ent, dir_ent->d_reclen);
+ count++;
+ }
+
+ if(errno) {
+ if(array)
+ while(count-- > 0)
+ free(array[count]);
+ free(array);
+ return -1;
+ }
+
+ // from here we can set the old errno back
+ errno = old_errno;
+
+ if(compare)
+ qsort(array, count, sizeof(struct dirent*),
+ (int (*)(const void *, const void *)) compare);
+ *res = array;
+ return count;
+}
+void seekdir(DIR *, long) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+long telldir(DIR *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int versionsort(const struct dirent **a, const struct dirent **b) {
+ return strverscmp((*a)->d_name, (*b)->d_name);
+}
diff --git a/lib/mlibc/options/posix/generic/dlfcn-stubs.cpp b/lib/mlibc/options/posix/generic/dlfcn-stubs.cpp
new file mode 100644
index 0000000..fc9fd84
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/dlfcn-stubs.cpp
@@ -0,0 +1,64 @@
+
+#include <bits/ensure.h>
+#include <dlfcn.h>
+
+#include <mlibc/debug.hpp>
+
+struct __dlapi_symbol {
+ const char *file;
+ void *base;
+ const char *symbol;
+ void *address;
+};
+
+extern "C" const char *__dlapi_error();
+extern "C" void *__dlapi_open(const char *, int, void *);
+extern "C" void *__dlapi_resolve(void *, const char *, void *);
+extern "C" int __dlapi_reverse(const void *, __dlapi_symbol *);
+extern "C" int __dlapi_close(void *);
+
+int dlclose(void *handle) {
+ return __dlapi_close(handle);
+}
+
+char *dlerror(void) {
+ return const_cast<char *>(__dlapi_error());
+}
+
+[[gnu::noinline]]
+void *dlopen(const char *file, int flags) {
+ auto ra = __builtin_extract_return_addr(__builtin_return_address(0));
+ return __dlapi_open(file, flags, ra);
+}
+
+[[gnu::noinline]]
+void *dlsym(void *__restrict handle, const char *__restrict string) {
+ auto ra = __builtin_extract_return_addr(__builtin_return_address(0));
+ return __dlapi_resolve(handle, string, ra);
+}
+
+[[gnu::noinline]]
+void *dlvsym(void *__restrict handle, const char *__restrict string, const char *__restrict version) {
+ mlibc::infoLogger() << "mlibc: dlvsym ignores version " << version << frg::endlog;
+ auto ra = __builtin_extract_return_addr(__builtin_return_address(0));
+ return __dlapi_resolve(handle, string, ra);
+}
+
+//gnu extensions
+int dladdr(const void *ptr, Dl_info *out) {
+ __dlapi_symbol info;
+ if(__dlapi_reverse(ptr, &info))
+ return 0;
+
+ out->dli_fname = info.file;
+ out->dli_fbase = info.base;
+ out->dli_sname = info.symbol;
+ out->dli_saddr = info.address;
+ return 1;
+}
+
+int dlinfo(void *, int, void *) {
+ __ensure(!"dlinfo() not implemented");
+ __builtin_unreachable();
+}
+
diff --git a/lib/mlibc/options/posix/generic/fcntl-stubs.cpp b/lib/mlibc/options/posix/generic/fcntl-stubs.cpp
new file mode 100644
index 0000000..66e2d12
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/fcntl-stubs.cpp
@@ -0,0 +1,108 @@
+
+#include <errno.h>
+#include <bits/ensure.h>
+#include <fcntl.h>
+#include <stdarg.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int creat(const char *pathname, mode_t mode) {
+ return open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode);
+}
+
+int fallocate(int, int, off_t, off_t) {
+ mlibc::infoLogger() << "mlibc: fallocate() is a no-op" << frg::endlog;
+ errno = ENOSYS;
+ return -1;
+}
+
+int fcntl(int fd, int command, ...) {
+ va_list args;
+ va_start(args, command);
+ int result;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fcntl, -1);
+ if(int e = mlibc::sys_fcntl(fd, command, args, &result); e) {
+ errno = e;
+ return -1;
+ }
+ va_end(args);
+ return result;
+}
+
+int openat(int dirfd, const char *pathname, int flags, ...) {
+ va_list args;
+ va_start(args, flags);
+ mode_t mode = 0;
+ int fd;
+
+ if((flags & (O_CREAT | O_TMPFILE)))
+ mode = va_arg(args, mode_t);
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_openat, -1);
+ if(int e = mlibc::sys_openat(dirfd, pathname, flags, mode, &fd); e) {
+ errno = e;
+ return -1;
+ }
+ va_end(args);
+ return fd;
+}
+
+int posix_fadvise(int fd, off_t offset, off_t length, int advice) {
+ if(!mlibc::sys_fadvise) {
+ mlibc::infoLogger() << "mlibc: fadvise() ignored due to missing sysdep" << frg::endlog;
+ return 0;
+ }
+
+ // posix_fadvise() returns an error instead of setting errno.
+ return mlibc::sys_fadvise(fd, offset, length, advice);
+}
+
+int posix_fallocate(int fd, off_t offset, off_t size) {
+ // posix_fallocate() returns an error instead of setting errno.
+ if(!mlibc::sys_fallocate) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+ return mlibc::sys_fallocate(fd, offset, size);
+}
+
+// This is a linux extension
+int name_to_handle_at(int, const char *, struct file_handle *, int *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int open_by_handle_at(int, struct file_handle *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+ssize_t splice(int, off_t *, int, off_t *, size_t, unsigned int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+ssize_t vmsplice(int, const struct iovec *, size_t, unsigned int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int open(const char *pathname, int flags, ...) {
+ mode_t mode = 0;
+
+ if ((flags & O_CREAT) || (flags & O_TMPFILE)) {
+ va_list args;
+ va_start(args, flags);
+ mode = va_arg(args, mode_t);
+ va_end(args);
+ }
+
+ int fd;
+ if(int e = mlibc::sys_open(pathname, flags, mode, &fd); e) {
+ errno = e;
+ return -1;
+ }
+ return fd;
+}
+
diff --git a/lib/mlibc/options/posix/generic/ftw-stubs.cpp b/lib/mlibc/options/posix/generic/ftw-stubs.cpp
new file mode 100644
index 0000000..2d93995
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/ftw-stubs.cpp
@@ -0,0 +1,18 @@
+
+#include <ftw.h>
+
+#include <bits/ensure.h>
+
+int ftw(const char *, int (*fn)(const char *, const struct stat *, int), int) {
+ (void)fn;
+ __ensure(!"ftw() not implemented");
+ __builtin_unreachable();
+}
+
+int nftw(const char *, int (*fn)(const char *, const struct stat *, int, struct FTW *),
+ int, int) {
+ (void)fn;
+ __ensure(!"nftw() not implemented");
+ __builtin_unreachable();
+}
+
diff --git a/lib/mlibc/options/posix/generic/grp-stubs.cpp b/lib/mlibc/options/posix/generic/grp-stubs.cpp
new file mode 100644
index 0000000..f8b2813
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/grp-stubs.cpp
@@ -0,0 +1,316 @@
+
+#include <grp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bits/ensure.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+namespace {
+ FILE *global_file;
+
+ bool open_global_file() {
+ if(!global_file) {
+ global_file = fopen("/etc/group", "r");
+ if(!global_file) {
+ errno = EIO;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void close_global_file() {
+ if(global_file) {
+ fclose(global_file);
+ global_file = nullptr;
+ }
+ }
+
+ template<typename F>
+ void walk_segments(frg::string_view line, char delimiter, F fn) {
+ size_t s = 0;
+ while(true) {
+ size_t d = line.find_first(delimiter, s);
+ if(d == size_t(-1))
+ break;
+ auto chunk = line.sub_string(s, d - s);
+ fn(chunk);
+ s = d + 1;
+ }
+ if(line[s]) {
+ auto chunk = line.sub_string(s, line.size() - s);
+
+ if (chunk.size() > 0) {
+ // Remove trailing newline
+ if (chunk[chunk.size() - 1] == '\n')
+ chunk = chunk.sub_string(0, chunk.size() - 1);
+
+ fn(chunk);
+ }
+ }
+ }
+
+ bool extract_entry(frg::string_view line, group *entry) {
+ frg::string_view segments[5];
+
+ // Parse the line into 3 or 4 segments (depending if the group has members or not)
+ int n = 0;
+ walk_segments(line, ':', [&] (frg::string_view s) {
+ __ensure(n < 4);
+ segments[n++] = s;
+ });
+
+ if(n < 3) // n can be 3 when there are no members in the group
+ return false;
+
+ // TODO: Handle strndup() and malloc() failure.
+ auto name = strndup(segments[0].data(), segments[0].size());
+ __ensure(name);
+
+ auto passwd = strndup(segments[1].data(), segments[1].size());
+
+ auto gid = segments[2].to_number<int>();
+ if(!gid)
+ return false;
+
+ size_t n_members = 0;
+ walk_segments(segments[3], ',', [&] (frg::string_view) {
+ n_members++;
+ });
+
+ auto members = reinterpret_cast<char **>(malloc(sizeof(char *) * (n_members + 1)));
+ __ensure(members);
+ size_t k = 0;
+ walk_segments(segments[3], ',', [&] (frg::string_view m) {
+ members[k] = strndup(m.data(), m.size());
+ __ensure(members[k]);
+ k++;
+ });
+ members[k] = nullptr;
+
+ entry->gr_name = name;
+ entry->gr_passwd = passwd;
+ entry->gr_gid = *gid;
+ entry->gr_mem = members;
+ return true;
+ }
+
+ void clear_entry(group *entry) {
+ free(entry->gr_name);
+ if(entry->gr_mem) {
+ for(size_t i = 0; entry->gr_mem[i]; i++)
+ free(entry->gr_mem[i]);
+ free(entry->gr_mem);
+ }
+ entry->gr_name = nullptr;
+ entry->gr_mem = nullptr;
+ }
+
+ template<typename C>
+ int walk_file(struct group *entry, C cond) {
+ auto file = fopen("/etc/group", "r");
+ if(!file) {
+ return EIO;
+ }
+
+ char line[512];
+ while(fgets(line, 512, file)) {
+ if(!extract_entry(line, entry))
+ continue;
+ if(cond(entry)) {
+ fclose(file);
+ return 0;
+ }
+ }
+
+ int err = ESRCH;
+ if(ferror(file)) {
+ err = EIO;
+ }
+
+ fclose(file);
+ return err;
+ }
+
+ int copy_to_buffer(struct group *grp, char *buffer, size_t size) {
+ // Adjust to correct alignment so that we can put gr_mem first in buffer
+ uintptr_t mask = sizeof(char *) - 1;
+ size_t offset = (reinterpret_cast<uintptr_t>(buffer) % sizeof(char *) + mask) & ~mask;
+ if (size < offset)
+ return ERANGE;
+
+ buffer += offset;
+ size -= offset;
+
+ // Calculate the amount of space we need
+ size_t nmemb, required_size = 0;
+ for (nmemb = 0; grp->gr_mem[nmemb] != nullptr; nmemb++) {
+ // One for the string's null terminator and one for the pointer in gr_mem
+ required_size += strlen(grp->gr_mem[nmemb]) + 1 + sizeof(char *);
+ }
+
+ // One for null terminator of gr_name, plus sizeof(char *) for nullptr terminator of gr_mem
+ required_size += strlen(grp->gr_name) + 1 + sizeof(char *);
+ if (size < required_size)
+ return ERANGE;
+
+ // Put the gr_mem array first in the buffer as we are guaranteed
+ // that the pointer is aligned correctly
+ char *string_data = buffer + (nmemb + 1) * sizeof(char *);
+
+ for (size_t i = 0; i < nmemb; i++) {
+ reinterpret_cast<char **>(buffer)[i] = string_data;
+ string_data = stpcpy(string_data, grp->gr_mem[i]) + 1;
+ free(grp->gr_mem[i]);
+ }
+
+ reinterpret_cast<char **>(buffer)[nmemb] = nullptr;
+ free(grp->gr_mem);
+ grp->gr_mem = reinterpret_cast<char **>(buffer);
+
+ char *gr_name = stpcpy(string_data, grp->gr_name) + 1;
+ free(grp->gr_name);
+ grp->gr_name = string_data;
+
+ __ensure(gr_name <= buffer + size);
+ return 0;
+ }
+}
+
+void endgrent(void) {
+ close_global_file();
+}
+
+struct group *getgrent(void) {
+ static group entry;
+ char line[512];
+
+ if(!open_global_file()) {
+ return nullptr;
+ }
+
+ if(fgets(line, 512, global_file)) {
+ clear_entry(&entry);
+ if(!extract_entry(line, &entry)) {
+ errno = EINVAL;
+ return nullptr;
+ }
+ return &entry;
+ }
+
+ if(ferror(global_file)) {
+ errno = EIO;
+ }
+
+ return nullptr;
+}
+
+struct group *getgrgid(gid_t gid) {
+ static group entry;
+
+ int err = walk_file(&entry, [&] (group *entry) {
+ return entry->gr_gid == gid;
+ });
+
+ if (err) {
+ errno = err;
+ return nullptr;
+ }
+
+ return &entry;
+}
+
+int getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t size, struct group **result) {
+ *result = nullptr;
+ int err = walk_file(grp, [&] (group *entry) {
+ return entry->gr_gid == gid;
+ });
+
+ if (err) {
+ return err;
+ }
+
+ err = copy_to_buffer(grp, buffer, size);
+ if (err) {
+ return err;
+ }
+
+ *result = grp;
+ return 0;
+}
+
+struct group *getgrnam(const char *name) {
+ static group entry;
+
+ int err = walk_file(&entry, [&] (group *entry) {
+ return !strcmp(entry->gr_name, name);
+ });
+
+ if (err) {
+ errno = err;
+ return nullptr;
+ }
+
+ return &entry;
+}
+
+int getgrnam_r(const char *name, struct group *grp, char *buffer, size_t size, struct group **result) {
+ *result = nullptr;
+
+ int err = walk_file(grp, [&] (group *entry) {
+ return !strcmp(entry->gr_name, name);
+ });
+
+ if (err) {
+ return err;
+ }
+
+ err = copy_to_buffer(grp, buffer, size);
+ if (err) {
+ return err;
+ }
+
+ *result = grp;
+ return 0;
+}
+
+void setgrent(void) {
+ if(!open_global_file()) {
+ return;
+ }
+ rewind(global_file);
+}
+
+int setgroups(size_t size, const gid_t *list) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setgroups, -1);
+ if(int e = mlibc::sys_setgroups(size, list); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int initgroups(const char *, gid_t) {
+ mlibc::infoLogger() << "mlibc: initgroups is a stub" << frg::endlog;
+ return 0;
+}
+
+int putgrent(const struct group *, FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct group *fgetgrent(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int getgrouplist(const char *, gid_t, gid_t *, int *) {
+ mlibc::infoLogger() << "mlibc: getgrouplist is a stub" << frg::endlog;
+ return 0;
+}
diff --git a/lib/mlibc/options/posix/generic/langinfo-stubs.cpp b/lib/mlibc/options/posix/generic/langinfo-stubs.cpp
new file mode 100644
index 0000000..b239cbd
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/langinfo-stubs.cpp
@@ -0,0 +1,15 @@
+
+#include <langinfo.h>
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/locale.hpp>
+
+char *nl_langinfo(nl_item item) {
+ return mlibc::nl_langinfo(item);
+}
+
+char *nl_langinfo_l(nl_item, locale_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
diff --git a/lib/mlibc/options/posix/generic/libgen-stubs.cpp b/lib/mlibc/options/posix/generic/libgen-stubs.cpp
new file mode 100644
index 0000000..ff80349
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/libgen-stubs.cpp
@@ -0,0 +1,51 @@
+
+#include <bits/ensure.h>
+#include <libgen.h>
+#include <string.h>
+
+#include <mlibc/debug.hpp>
+
+// Adopted from musl's code.
+char *basename(char *s) {
+ // This empty string behavior is specified by POSIX.
+ if (!s || !*s)
+ return const_cast<char *>(".");
+
+ // Delete trailing slashes.
+ // Note that we do not delete the slash at index zero.
+ auto i = strlen(s) - 1;
+ for(; i && s[i] == '/'; i--)
+ s[i] = 0;
+
+ // Find the last non-trailing slash.
+ for(; i && s[i - 1] != '/'; i--)
+ ;
+ return s + i;
+}
+
+char *dirname(char *s) {
+ if (!s || !(*s))
+ return const_cast<char *>(".");
+
+ auto i = strlen(s) - 1;
+
+ // Skip trailing slashes.
+ for (; s[i] == '/'; i--)
+ if(!i) // Path only consists of slashes.
+ return const_cast<char *>("/");
+
+ // Skip the last non-slash path component.
+ for (; s[i] != '/'; i--)
+ if(!i) // Path only contains a single component.
+ return const_cast<char *>(".");
+
+ // Skip slashes.
+ for (; s[i] == '/'; i--)
+ if(!i) // Path is entry in root directory.
+ return const_cast<char *>("/");
+
+ s[i+1] = 0;
+
+ return s;
+}
+
diff --git a/lib/mlibc/options/posix/generic/lookup.cpp b/lib/mlibc/options/posix/generic/lookup.cpp
new file mode 100644
index 0000000..f877fe5
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/lookup.cpp
@@ -0,0 +1,512 @@
+#include <mlibc/lookup.hpp>
+#include <mlibc/resolv_conf.hpp>
+#include <mlibc/debug.hpp>
+#include <bits/ensure.h>
+
+#include <frg/string.hpp>
+#include <mlibc/allocator.hpp>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+namespace mlibc {
+
+namespace {
+ constexpr unsigned int RECORD_A = 1;
+ constexpr unsigned int RECORD_CNAME = 5;
+ constexpr unsigned int RECORD_PTR = 12;
+}
+
+static frg::string<MemoryAllocator> read_dns_name(char *buf, char *&it) {
+ frg::string<MemoryAllocator> res{getAllocator()};
+ while (true) {
+ char code = *it++;
+ if ((code & 0xC0) == 0xC0) {
+ // pointer
+ uint8_t offset = ((code & 0x3F) << 8) | *it++;
+ auto offset_it = buf + offset;
+ return res + read_dns_name(buf, offset_it);
+ } else if (!(code & 0xC0)) {
+ if (!code)
+ break;
+
+ for (int i = 0; i < code; i++)
+ res += (*it++);
+
+ if (*it)
+ res += '.';
+ } else {
+ break;
+ }
+ }
+
+ return res;
+}
+
+int lookup_name_dns(struct lookup_result &buf, const char *name,
+ frg::string<MemoryAllocator> &canon_name) {
+ frg::string<MemoryAllocator> request{getAllocator()};
+
+ int num_q = 1;
+ struct dns_header header;
+ header.identification = htons(123);
+ header.flags = htons(0x100);
+ header.no_q = htons(num_q);
+ header.no_ans = htons(0);
+ header.no_auths = htons(0);
+ header.no_additional = htons(0);
+
+ request.resize(sizeof(header));
+ memcpy(request.data(), &header, sizeof(header));
+
+ const char *end = name;
+ while (*end != '\0') {
+ end = strchrnul(name, '.');
+ size_t length = end - name;
+ frg::string_view substring{name, length};
+ name += length + 1;
+ request += char(length);
+ request += substring;
+ }
+
+ request += char(0);
+ // set question type to fetch A records
+ request += 0;
+ request += 1;
+ // set CLASS to IN
+ request += 0;
+ request += 1;
+
+ struct sockaddr_in sin = {};
+ sin.sin_family = AF_INET;
+ // TODO(geert): we could probably make this use the service lookup
+ // for dns
+ sin.sin_port = htons(53);
+
+ auto nameserver = get_nameserver();
+ if (!inet_aton(nameserver ? nameserver->name.data() : "127.0.0.1", &sin.sin_addr)) {
+ mlibc::infoLogger() << "lookup_name_dns(): inet_aton() failed!" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ mlibc::infoLogger() << "lookup_name_dns(): socket() failed" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ size_t sent = sendto(fd, request.data(), request.size(), 0,
+ (struct sockaddr*)&sin, sizeof(sin));
+ if (sent != request.size()) {
+ mlibc::infoLogger() << "lookup_name_dns(): sendto() failed to send everything" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ char response[256];
+ ssize_t rlen;
+ int num_ans = 0;
+ while ((rlen = recvfrom(fd, response, 256, 0, NULL, NULL)) >= 0) {
+ if ((size_t)rlen < sizeof(struct dns_header))
+ continue;
+ auto response_header = reinterpret_cast<struct dns_header*>(response);
+ if (response_header->identification != header.identification)
+ return -EAI_FAIL;
+
+ auto it = response + sizeof(struct dns_header);
+ for (int i = 0; i < ntohs(response_header->no_q); i++) {
+ auto dns_name = read_dns_name(response, it);
+ (void) dns_name;
+ it += 4;
+ }
+
+ for (int i = 0; i < ntohs(response_header->no_ans); i++) {
+ struct dns_addr_buf buffer;
+ auto dns_name = read_dns_name(response, it);
+
+ uint16_t rr_type = (it[0] << 8) | it[1];
+ uint16_t rr_class = (it[2] << 8) | it[3];
+ uint16_t rr_length = (it[8] << 8) | it[9];
+ it += 10;
+ (void)rr_class;
+
+ switch (rr_type) {
+ case RECORD_A:
+ memcpy(buffer.addr, it, rr_length);
+ it += rr_length;
+ buffer.family = AF_INET;
+ buffer.name = std::move(dns_name);
+ buf.buf.push(std::move(buffer));
+ break;
+ case RECORD_CNAME:
+ canon_name = read_dns_name(response, it);
+ buf.aliases.push(std::move(dns_name));
+ break;
+ default:
+ mlibc::infoLogger() << "lookup_name_dns: unknown rr type "
+ << rr_type << frg::endlog;
+ break;
+ }
+ }
+ num_ans += ntohs(response_header->no_ans);
+
+ if (num_ans >= num_q)
+ break;
+ }
+
+ close(fd);
+ return buf.buf.size();
+}
+
+int lookup_addr_dns(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family) {
+ frg::string<MemoryAllocator> request{getAllocator()};
+
+ int num_q = 1;
+ struct dns_header header;
+ header.identification = htons(123);
+ header.flags = htons(0x100);
+ header.no_q = htons(num_q);
+ header.no_ans = htons(0);
+ header.no_auths = htons(0);
+ header.no_additional = htons(0);
+
+ request.resize(sizeof(header));
+ memcpy(request.data(), &header, sizeof(header));
+
+ char addr_str[64];
+ if(!inet_ntop(family, addr.data(), addr_str, sizeof(addr_str))) {
+ switch(errno) {
+ case EAFNOSUPPORT:
+ return -EAI_FAMILY;
+ case ENOSPC:
+ return -EAI_OVERFLOW;
+ default:
+ return -EAI_FAIL;
+ }
+ }
+ frg::string<MemoryAllocator> req_str{getAllocator(), addr_str};
+ req_str += ".in-addr.arpa";
+
+ frg::string_view req_view{req_str.data(), req_str.size()};
+ size_t ptr = 0;
+ do {
+ size_t next = req_view.find_first('.', ptr);
+ size_t length = next != (size_t)-1 ? next - ptr : req_view.size() - ptr;
+ frg::string_view substring = req_view.sub_string(ptr, length);
+ request += char(length);
+ request += substring;
+ ptr = next + 1;
+ } while(ptr != 0);
+
+ request += char(0);
+ // set question type to fetch PTR records
+ request += 0;
+ request += 12;
+ // set CLASS to IN
+ request += 0;
+ request += 1;
+
+
+ struct sockaddr_in sin = {};
+ sin.sin_family = AF_INET;
+ // TODO(geert): we could probably make this use the service lookup
+ // for dns
+ sin.sin_port = htons(53);
+
+ auto nameserver = get_nameserver();
+ if (!inet_aton(nameserver ? nameserver->name.data() : "127.0.0.1", &sin.sin_addr)) {
+ mlibc::infoLogger() << "lookup_name_dns(): inet_aton() failed!" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ mlibc::infoLogger() << "lookup_name_dns(): socket() failed" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ size_t sent = sendto(fd, request.data(), request.size(), 0,
+ (struct sockaddr*)&sin, sizeof(sin));
+ if (sent != request.size()) {
+ mlibc::infoLogger() << "lookup_name_dns(): sendto() failed to send everything" << frg::endlog;
+ return -EAI_SYSTEM;
+ }
+
+ char response[256];
+ ssize_t rlen;
+ int num_ans = 0;
+ while ((rlen = recvfrom(fd, response, 256, 0, NULL, NULL)) >= 0) {
+ if ((size_t)rlen < sizeof(struct dns_header))
+ continue;
+ auto response_header = reinterpret_cast<struct dns_header*>(response);
+ if (response_header->identification != header.identification)
+ return -EAI_FAIL;
+
+ auto it = response + sizeof(struct dns_header);
+ for (int i = 0; i < ntohs(response_header->no_q); i++) {
+ auto dns_name = read_dns_name(response, it);
+ (void) dns_name;
+ it += 4;
+ }
+
+ for (int i = 0; i < ntohs(response_header->no_ans); i++) {
+ struct dns_addr_buf buffer;
+ auto dns_name = read_dns_name(response, it);
+
+ uint16_t rr_type = (it[0] << 8) | it[1];
+ uint16_t rr_class = (it[2] << 8) | it[3];
+ uint16_t rr_length = (it[8] << 8) | it[9];
+ it += 10;
+ (void)rr_class;
+ (void)rr_length;
+
+ (void)dns_name;
+
+ switch (rr_type) {
+ case RECORD_PTR: {
+ auto ptr_name = read_dns_name(response, it);
+ if (ptr_name.size() >= name.size())
+ return -EAI_OVERFLOW;
+ std::copy(ptr_name.begin(), ptr_name.end(), name.data());
+ name.data()[ptr_name.size()] = '\0';
+ return 1;
+ }
+ default:
+ mlibc::infoLogger() << "lookup_addr_dns: unknown rr type "
+ << rr_type << frg::endlog;
+ break;
+ }
+ num_ans += ntohs(response_header->no_ans);
+
+ if (num_ans >= num_q)
+ break;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+int lookup_name_hosts(struct lookup_result &buf, const char *name,
+ frg::string<MemoryAllocator> &canon_name) {
+ auto file = fopen("/etc/hosts", "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ char line[128];
+ int name_length = strlen(name);
+ while (fgets(line, 128, file)) {
+ char *pos;
+ // same way to deal with comments as in services.cpp
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+
+ for(pos = line + 1; (pos = strstr(pos, name)) &&
+ (!isspace(pos[-1]) || !isspace(pos[name_length])); pos++);
+ if (!pos)
+ continue;
+
+ for (pos = line; !isspace(*pos); pos++);
+ *pos = '\0';
+
+ // TODO(geert): we assume ipv4 for now
+ struct in_addr addr;
+ if (!inet_aton(line, &addr))
+ continue;
+
+ pos++;
+ for(; *pos && isspace(*pos); pos++);
+ char *end;
+ for(end = pos; *end && !isspace(*end); end++);
+
+ struct dns_addr_buf buffer;
+ memcpy(buffer.addr, &addr, 4);
+ buffer.family = AF_INET;
+ buffer.name = frg::string<MemoryAllocator>{pos,
+ static_cast<size_t>(end - pos), getAllocator()};
+ canon_name = buffer.name;
+
+ buf.buf.push(std::move(buffer));
+
+ pos = end;
+ while (pos[1]) {
+ for (; *pos && isspace(*pos); pos++);
+ for (end = pos; *end && !isspace(*end); end++);
+ auto name = frg::string<MemoryAllocator>{pos,
+ static_cast<size_t>(end - pos), getAllocator()};
+ buf.aliases.push(std::move(name));
+ pos = end;
+ }
+ }
+
+ fclose(file);
+ return buf.buf.size();
+}
+
+int lookup_addr_hosts(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family) {
+ auto file = fopen("/etc/hosts", "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ // Buffer to hold ASCII version of address
+ char addr_str[64];
+ if(!inet_ntop(family, addr.data(), addr_str, sizeof(addr_str))) {
+ switch(errno) {
+ case EAFNOSUPPORT:
+ return -EAI_FAMILY;
+ case ENOSPC:
+ return -EAI_OVERFLOW;
+ default:
+ return -EAI_FAIL;
+ }
+ }
+ int addr_str_len = strlen(addr_str);
+
+ char line[128];
+ while (fgets(line, 128, file)) {
+ char *pos;
+ // same way to deal with comments as in services.cpp
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+ if (strncmp(line, addr_str, addr_str_len))
+ continue;
+
+ for (pos = line + addr_str_len + 1; isspace(*pos); pos++);
+ char *begin = pos;
+ for (; !isspace(*pos); pos++);
+ char *end = pos;
+
+ size_t size = end - begin;
+ if (size >= name.size())
+ return -EAI_OVERFLOW;
+ std::copy(begin, end, name.data());
+ name.data()[size] = '\0';
+ return 1;
+ }
+ return 0;
+}
+
+int lookup_name_null(struct lookup_result &buf, int flags, int family) {
+ if (flags & AI_PASSIVE) {
+ if (family != AF_INET6) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+
+ in_addr_t addr = INADDR_ANY;
+ memcpy(&addr_buf.addr, &addr, 4);
+
+ buf.buf.push_back(addr_buf);
+ }
+ if (family != AF_INET) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+
+ struct in6_addr addr = IN6ADDR_ANY_INIT;
+ memcpy(&addr_buf.addr, &addr, 16);
+
+ buf.buf.push_back(addr_buf);
+ }
+ } else {
+ if (family != AF_INET6) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+
+ in_addr_t addr = INADDR_LOOPBACK;
+ memcpy(&addr_buf.addr, &addr, 4);
+
+ buf.buf.push_back(addr_buf);
+ }
+ if (family != AF_INET) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+
+ struct in6_addr addr = IN6ADDR_LOOPBACK_INIT;
+ memcpy(&addr_buf.addr, &addr, 16);
+
+ buf.buf.push_back(addr_buf);
+ }
+ }
+ return buf.buf.size();
+}
+
+int lookup_name_ip(struct lookup_result &buf, const char *name, int family) {
+ if (family == AF_INET) {
+ in_addr_t addr = 0;
+ int res = inet_pton(AF_INET, name, &addr);
+
+ if (res <= 0)
+ return -EAI_NONAME;
+
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+ memcpy(&addr_buf.addr, &addr, 4);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+ }
+
+ if (family == AF_INET6) {
+ struct in6_addr addr{0};
+ int res = inet_pton(AF_INET6, name, &addr);
+
+ if (res <= 0)
+ return -EAI_NONAME;
+
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+ memcpy(&addr_buf.addr, &addr, 16);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+ }
+
+ // If no family was specified we try ipv4 and then ipv6.
+ in_addr_t addr4 = 0;
+ int res = inet_pton(AF_INET, name, &addr4);
+
+ if (res > 0) {
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET;
+ memcpy(&addr_buf.addr, &addr4, 4);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+ }
+
+ struct in6_addr addr6{0};
+ res = inet_pton(AF_INET6, name, &addr6);
+
+ if (res <= 0)
+ return -EAI_NONAME;
+
+ struct dns_addr_buf addr_buf;
+ addr_buf.family = AF_INET6;
+ memcpy(&addr_buf.addr, &addr6, 16);
+
+ buf.buf.push_back(addr_buf);
+ return 1;
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/posix/generic/mqueue.cpp b/lib/mlibc/options/posix/generic/mqueue.cpp
new file mode 100644
index 0000000..d635419
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/mqueue.cpp
@@ -0,0 +1,22 @@
+#include <mqueue.h>
+#include <bits/ensure.h>
+
+int mq_getattr(mqd_t, struct mq_attr *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int mq_setattr(mqd_t, const struct mq_attr *__restrict__, struct mq_attr *__restrict__) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int mq_unlink(const char *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+mqd_t mq_open(const char *, int, ...) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/net-if-stubs.cpp b/lib/mlibc/options/posix/generic/net-if-stubs.cpp
new file mode 100644
index 0000000..6a65a5c
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/net-if-stubs.cpp
@@ -0,0 +1,40 @@
+#include <errno.h>
+#include <net/if.h>
+#include <stdlib.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+void if_freenameindex(struct if_nameindex *) {
+ mlibc::infoLogger() << "mlibc: if_freenameindex is a no-op" << frg::endlog;
+}
+
+char *if_indextoname(unsigned int index, char *name) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_if_indextoname, NULL);
+
+ if(int e = sysdep(index, name); e) {
+ errno = e;
+ return NULL;
+ }
+
+ return name;
+}
+
+struct if_nameindex *if_nameindex(void) {
+ mlibc::infoLogger() << "mlibc: if_nameindex() is a no-op" << frg::endlog;
+ errno = ENOSYS;
+ return NULL;
+}
+
+unsigned int if_nametoindex(const char *name) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_if_nametoindex, 0);
+ unsigned int ret = 0;
+
+ if(int e = sysdep(name, &ret); e) {
+ errno = e;
+ return 0;
+ }
+
+ return ret;
+}
diff --git a/lib/mlibc/options/posix/generic/netdb-stubs.cpp b/lib/mlibc/options/posix/generic/netdb-stubs.cpp
new file mode 100644
index 0000000..455444b
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/netdb-stubs.cpp
@@ -0,0 +1,486 @@
+#include <netdb.h>
+#include <bits/ensure.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/lookup.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/services.hpp>
+#include <frg/vector.hpp>
+#include <frg/array.hpp>
+#include <frg/span.hpp>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <errno.h>
+
+__thread int __mlibc_h_errno;
+
+// This function is from musl
+int *__h_errno_location(void) {
+ return &__mlibc_h_errno;
+}
+
+void endhostent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void endnetent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void endprotoent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void endservent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void freeaddrinfo(struct addrinfo *ptr) {
+ if (ptr) {
+ auto buf = (struct mlibc::ai_buf*) ptr - offsetof(struct mlibc::ai_buf, ai);
+ // this string was allocated by a frg::string
+ getAllocator().free(ptr->ai_canonname);
+ free(buf);
+ }
+}
+
+const char *gai_strerror(int code) {
+ static thread_local char buffer[128];
+ snprintf(buffer, sizeof(buffer), "Unknown error (%d)", code);
+ return buffer;
+}
+
+int getaddrinfo(const char *__restrict node, const char *__restrict service,
+ const struct addrinfo *__restrict hints, struct addrinfo **__restrict res) {
+ if (!node && !service)
+ return EAI_NONAME;
+
+ int socktype = 0, protocol = 0, family = AF_UNSPEC, flags = AI_V4MAPPED | AI_ADDRCONFIG;
+ if (hints) {
+ socktype = hints->ai_socktype;
+ protocol = hints->ai_protocol;
+ family = hints->ai_family;
+ flags = hints->ai_flags;
+
+ int mask = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICHOST | AI_PASSIVE |
+ AI_CANONNAME | AI_ALL | AI_NUMERICSERV;
+ if ((flags & mask) != flags)
+ return EAI_BADFLAGS;
+
+ if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
+ return EAI_FAMILY;
+ }
+
+ mlibc::service_result serv_buf{getAllocator()};
+ int serv_count = mlibc::lookup_serv_by_name(serv_buf, service, protocol, socktype, flags);
+ if (serv_count < 0)
+ return -serv_count;
+
+ struct mlibc::lookup_result addr_buf;
+ int addr_count = 1;
+ frg::string<MemoryAllocator> canon{getAllocator()};
+ if (node) {
+ if ((addr_count = mlibc::lookup_name_ip(addr_buf, node, family)) <= 0) {
+ if (flags & AI_NUMERICHOST)
+ addr_count = -EAI_NONAME;
+ else if ((addr_count = mlibc::lookup_name_hosts(addr_buf, node, canon)) <= 0)
+ addr_count = mlibc::lookup_name_dns(addr_buf, node, canon);
+ else
+ addr_count = 1;
+ }
+
+ if (addr_count < 0)
+ return -addr_count;
+ if (!addr_count)
+ return EAI_NONAME;
+ } else {
+ /* There is no node specified */
+ if (flags & AI_NUMERICHOST)
+ return EAI_NONAME;
+ addr_count = lookup_name_null(addr_buf, flags, family);
+ }
+
+ auto out = (struct mlibc::ai_buf *) calloc(serv_count * addr_count,
+ sizeof(struct mlibc::ai_buf));
+
+ if (node && !canon.size())
+ canon = frg::string<MemoryAllocator>{node, getAllocator()};
+
+ for (int i = 0, k = 0; i < addr_count; i++) {
+ for (int j = 0; j < serv_count; j++, k++) {
+ out[i].ai.ai_family = addr_buf.buf[i].family;
+ out[i].ai.ai_socktype = serv_buf[j].socktype;
+ out[i].ai.ai_protocol = serv_buf[j].protocol;
+ out[i].ai.ai_flags = flags;
+ out[i].ai.ai_addr = (struct sockaddr *) &out[i].sa;
+ if (canon.size())
+ out[i].ai.ai_canonname = canon.data();
+ else
+ out[i].ai.ai_canonname = NULL;
+ out[i].ai.ai_next = NULL;
+ switch (addr_buf.buf[i].family) {
+ case AF_INET:
+ out[i].ai.ai_addrlen = sizeof(struct sockaddr_in);
+ out[i].sa.sin.sin_port = htons(serv_buf[j].port);
+ out[i].sa.sin.sin_family = AF_INET;
+ memcpy(&out[i].sa.sin.sin_addr, addr_buf.buf[i].addr, 4);
+ break;
+ case AF_INET6:
+ out[i].ai.ai_addrlen = sizeof(struct sockaddr_in6);
+ out[i].sa.sin6.sin6_family = htons(serv_buf[j].port);
+ out[i].sa.sin6.sin6_family = AF_INET6;
+ memcpy(&out[i].sa.sin6.sin6_addr, addr_buf.buf[i].addr, 16);
+ break;
+ }
+ }
+ }
+ if (canon.size())
+ canon.detach();
+
+ *res = &out[0].ai;
+ return 0;
+}
+
+struct hostent *gethostent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int getnameinfo(const struct sockaddr *__restrict addr, socklen_t addr_len,
+ char *__restrict host, socklen_t host_len, char *__restrict serv,
+ socklen_t serv_len, int flags) {
+ frg::array<uint8_t, 16> addr_array;
+ int family = addr->sa_family;
+
+ switch(family) {
+ case AF_INET: {
+ if (addr_len < sizeof(struct sockaddr_in))
+ return EAI_FAMILY;
+ auto sockaddr = reinterpret_cast<const struct sockaddr_in*>(addr);
+ memcpy(addr_array.data(), reinterpret_cast<const char*>(&sockaddr->sin_addr), 4);
+ break;
+ }
+ case AF_INET6: {
+ mlibc::infoLogger() << "getnameinfo(): ipv6 is not fully supported in this function" << frg::endlog;
+ if (addr_len < sizeof(struct sockaddr_in6))
+ return EAI_FAMILY;
+ auto sockaddr = reinterpret_cast<const struct sockaddr_in6*>(addr);
+ memcpy(addr_array.data(), reinterpret_cast<const char*>(&sockaddr->sin6_addr), 16);
+ break;
+ }
+ default:
+ return EAI_FAMILY;
+ }
+
+ if (host && host_len) {
+ frg::span<char> host_span{host, host_len};
+ int res = 0;
+ if (!(flags & NI_NUMERICHOST))
+ res = mlibc::lookup_addr_hosts(host_span, addr_array, family);
+ if (!(flags & NI_NUMERICHOST) && !res)
+ res = mlibc::lookup_addr_dns(host_span, addr_array, family);
+
+ if (!res) {
+ if (flags & NI_NAMEREQD)
+ return EAI_NONAME;
+ if(!inet_ntop(family, addr_array.data(), host, host_len)) {
+ switch(errno) {
+ case EAFNOSUPPORT:
+ return EAI_FAMILY;
+ case ENOSPC:
+ return EAI_OVERFLOW;
+ default:
+ return EAI_FAIL;
+ }
+ }
+ }
+
+ if (res < 0)
+ return -res;
+ }
+
+ if (serv && serv_len) {
+ __ensure("getnameinfo(): not implemented service resolution yet!");
+ __builtin_unreachable();
+ }
+
+ return 0;
+}
+
+struct netent *getnetbyaddr(uint32_t, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct netent *getnetbyname(const char *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct netent *getnetent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct hostent *gethostbyname(const char *name) {
+ if (!name) {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ struct mlibc::lookup_result buf;
+ frg::string<MemoryAllocator> canon{getAllocator()};
+ int ret = 0;
+ if ((ret = mlibc::lookup_name_hosts(buf, name, canon)) <= 0)
+ ret = mlibc::lookup_name_dns(buf, name, canon);
+ if (ret <= 0) {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ static struct hostent h;
+ if (h.h_name) {
+ getAllocator().free(h.h_name);
+ for (int i = 0; h.h_aliases[i] != NULL; i++)
+ getAllocator().free(h.h_aliases[i]);
+ free(h.h_aliases);
+
+ if (h.h_addr_list) {
+ for (int i = 0; h.h_addr_list[i] != NULL; i++)
+ free(h.h_addr_list[i]);
+ free(h.h_addr_list);
+ }
+ }
+ h = {};
+
+ if (!canon.size())
+ canon = frg::string<MemoryAllocator>{name, getAllocator()};
+
+ h.h_name = canon.data();
+
+ h.h_aliases = reinterpret_cast<char**>(malloc((buf.aliases.size() + 1)
+ * sizeof(char*)));
+ int alias_pos = 0;
+ for (auto &buf_name : buf.aliases) {
+ h.h_aliases[alias_pos] = buf_name.data();
+ buf_name.detach();
+ alias_pos++;
+ }
+ h.h_aliases[alias_pos] = NULL;
+ canon.detach();
+
+ // just pick the first family as the one for all addresses...??
+ h.h_addrtype = buf.buf[0].family;
+ if (h.h_addrtype != AF_INET && h.h_addrtype != AF_INET6) {
+ // this is not allowed per spec
+ h_errno = NO_DATA;
+ return NULL;
+ }
+
+ // can only be AF_INET or AF_INET6
+ h.h_length = h.h_addrtype == AF_INET ? 4 : 16;
+ h.h_addr_list = reinterpret_cast<char**>(malloc((ret + 1) * sizeof(char*)));
+ int addr_pos = 0;
+ for (int i = 0; i < ret; i++) {
+ if (buf.buf[i].family != h.h_addrtype)
+ continue;
+ h.h_addr_list[addr_pos] = reinterpret_cast<char*>(malloc(h.h_length));
+ memcpy(h.h_addr_list[addr_pos], buf.buf[i].addr, h.h_length);
+ addr_pos++;
+ }
+ h.h_addr_list[addr_pos] = NULL;
+
+ return &h;
+}
+
+struct hostent *gethostbyname2(const char *, int) {
+ __ensure(!"gethostbyname2() not implemented");
+ __builtin_unreachable();
+}
+
+struct hostent *gethostbyaddr(const void *, socklen_t, int) {
+ __ensure(!"gethostbyaddr() not implemented");
+ __builtin_unreachable();
+}
+
+int gethostbyaddr_r(const void *__restrict, socklen_t, int, struct hostent *__restrict,
+ char *__restrict, size_t, struct hostent **__restrict, int *__restrict) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int gethostbyname_r(const char *__restrict, struct hostent *__restrict, char *__restrict, size_t,
+ struct hostent **__restrict, int *__restrict) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct protoent *getprotobyname(const char *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct protoent *getprotobynumber(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct protoent *getprotoent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct servent *getservbyname(const char *name, const char *proto) {
+ int iproto = -1;
+ if (proto &&(!strncmp(proto, "tcp", 3) || !strncmp(proto, "TCP", 3)))
+ iproto = IPPROTO_TCP;
+ else if (proto && (!strncmp(proto, "udp", 3) || !strncmp(proto, "UDP", 3)))
+ iproto = IPPROTO_UDP;
+
+ static struct servent ret;
+ if (ret.s_name) {
+ free(ret.s_name);
+ ret.s_name = nullptr;
+
+ for (char **alias = ret.s_aliases; *alias != NULL; alias++) {
+ free(*alias);
+ *alias = nullptr;
+ }
+
+ free(ret.s_proto);
+ ret.s_proto = nullptr;
+ }
+
+ mlibc::service_result serv_buf{getAllocator()};
+ int count = mlibc::lookup_serv_by_name(serv_buf, name, iproto,
+ 0, 0);
+ if (count <= 0)
+ return NULL;
+
+ ret.s_name = serv_buf[0].name.data();
+ serv_buf[0].name.detach();
+ // Sanity check.
+ if (strncmp(name, serv_buf[0].name.data(), serv_buf[0].name.size()))
+ return NULL;
+
+ ret.s_aliases = reinterpret_cast<char**>(malloc((serv_buf[0].aliases.size() + 1) * sizeof(char*)));
+ int alias_pos = 0;
+ for (auto &buf_name : serv_buf[0].aliases) {
+ ret.s_aliases[alias_pos] = buf_name.data();
+ buf_name.detach();
+ alias_pos++;
+ }
+ ret.s_aliases[alias_pos] = NULL;
+
+ ret.s_port = htons(serv_buf[0].port);
+
+ auto proto_string = frg::string<MemoryAllocator>(getAllocator());
+ if (!proto) {
+ if (serv_buf[0].protocol == IPPROTO_TCP)
+ proto_string = frg::string<MemoryAllocator>("tcp", getAllocator());
+ else if (serv_buf[0].protocol == IPPROTO_UDP)
+ proto_string = frg::string<MemoryAllocator>("udp", getAllocator());
+ else
+ return NULL;
+ } else {
+ proto_string = frg::string<MemoryAllocator>(proto, getAllocator());
+ }
+ ret.s_proto = proto_string.data();
+ proto_string.detach();
+
+ return &ret;
+}
+
+struct servent *getservbyport(int port, const char *proto) {
+ int iproto = -1;
+ if (proto && (!strncmp(proto, "tcp", 3) || !strncmp(proto, "TCP", 3)))
+ iproto = IPPROTO_TCP;
+ else if (proto && (!strncmp(proto, "udp", 3) || !strncmp(proto, "UDP", 3)))
+ iproto = IPPROTO_UDP;
+
+ static struct servent ret;
+ if (ret.s_name) {
+ free(ret.s_name);
+ ret.s_name = nullptr;
+
+ for (char **alias = ret.s_aliases; *alias != NULL; alias++) {
+ free(*alias);
+ *alias = nullptr;
+ }
+
+ free(ret.s_proto);
+ ret.s_proto = nullptr;
+ }
+
+ mlibc::service_result serv_buf{getAllocator()};
+ int count = mlibc::lookup_serv_by_port(serv_buf, iproto, ntohs(port));
+ if (count <= 0)
+ return NULL;
+
+ ret.s_name = serv_buf[0].name.data();
+ serv_buf[0].name.detach();
+
+ ret.s_aliases = reinterpret_cast<char**>(malloc((serv_buf[0].aliases.size() + 1) * sizeof(char*)));
+ int alias_pos = 0;
+ for (auto &buf_name : serv_buf[0].aliases) {
+ ret.s_aliases[alias_pos] = buf_name.data();
+ buf_name.detach();
+ alias_pos++;
+ }
+ ret.s_aliases[alias_pos] = NULL;
+
+ ret.s_port = port;
+
+ auto proto_string = frg::string<MemoryAllocator>(getAllocator());
+ if (!proto) {
+ if (serv_buf[0].protocol == IPPROTO_TCP)
+ proto_string = frg::string<MemoryAllocator>("tcp", getAllocator());
+ else if (serv_buf[0].protocol == IPPROTO_UDP)
+ proto_string = frg::string<MemoryAllocator>("udp", getAllocator());
+ else
+ return NULL;
+ } else {
+ proto_string = frg::string<MemoryAllocator>(proto, getAllocator());
+ }
+ ret.s_proto = proto_string.data();
+ proto_string.detach();
+
+ return &ret;
+}
+
+struct servent *getservent(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void sethostent(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void setnetent(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void setprotoent(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void setservent(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+const char *hstrerror(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/poll.cpp b/lib/mlibc/options/posix/generic/poll.cpp
new file mode 100644
index 0000000..c34e25e
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/poll.cpp
@@ -0,0 +1,31 @@
+
+#include <errno.h>
+#include <poll.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int poll(struct pollfd *fds, nfds_t count, int timeout) {
+ int num_events;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_poll, -1);
+ if(int e = mlibc::sys_poll(fds, count, timeout, &num_events); e) {
+ errno = e;
+ return -1;
+ }
+ return num_events;
+}
+
+#if __MLIBC_LINUX_OPTION
+int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask) {
+ sigset_t origmask;
+ int timeout = (timeout_ts == NULL) ? -1 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
+
+ sigprocmask(SIG_SETMASK, sigmask, &origmask);
+ int ready = poll(fds, nfds, timeout);
+ sigprocmask(SIG_SETMASK, &origmask, NULL);
+
+ return ready;
+}
+#endif // __MLIBC_LINUX_OPTION
+
diff --git a/lib/mlibc/options/posix/generic/posix-file-io.cpp b/lib/mlibc/options/posix/generic/posix-file-io.cpp
new file mode 100644
index 0000000..1a4f38b
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix-file-io.cpp
@@ -0,0 +1,275 @@
+#include <mlibc/posix-file-io.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+#include <errno.h>
+
+namespace mlibc {
+
+int mem_file::reopen(const char *, const char *) {
+ mlibc::panicLogger() << "mlibc: freopen() on a mem_file stream is unimplemented!" << frg::endlog;
+ return -1;
+}
+
+int mem_file::determine_type(stream_type *type) {
+ *type = stream_type::file_like;
+ return 0;
+}
+
+int mem_file::determine_bufmode(buffer_mode *mode) {
+ *mode = buffer_mode::no_buffer;
+ return 0;
+}
+
+memstream_mem_file::memstream_mem_file(char **ptr, size_t *sizeloc, int flags, void (*do_dispose)(abstract_file *))
+: mem_file{flags, do_dispose}, _bufloc{ptr}, _sizeloc{sizeloc} { }
+
+
+int memstream_mem_file::close() {
+ _update_ptrs();
+ _buf.detach();
+
+ return 0;
+}
+
+int memstream_mem_file::io_read(char *buffer, size_t max_size, size_t *actual_size) {
+ if ((_pos >= 0 && _pos >= _max_size) || !max_size) {
+ *actual_size = 0;
+ return 0;
+ }
+
+ size_t bytes_read = std::min(size_t(_max_size - _pos), max_size);
+ memcpy(buffer, _buffer().data() + _pos, bytes_read);
+ _pos += bytes_read;
+ *actual_size = bytes_read;
+ return 0;
+}
+
+int memstream_mem_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) {
+ if (_pos + max_size >= _buffer_size()) {
+ _buf.resize(_pos + max_size + 1, '\0');
+ _update_ptrs();
+ }
+
+ size_t bytes_write = std::min(static_cast<size_t>(_buffer_size() - _pos), max_size);
+ memcpy(_buffer().data() + _pos, buffer, bytes_write);
+ _pos += max_size;
+ *actual_size = max_size;
+
+ return 0;
+}
+
+int memstream_mem_file::io_seek(off_t offset, int whence, off_t *new_offset) {
+ switch (whence) {
+ case SEEK_SET:
+ _pos = offset;
+ if (_pos >= 0 && size_t(_pos) >= _buffer_size()) {
+ _buf.resize(_pos + 1, '\0');
+ _update_ptrs();
+ }
+ *new_offset = _pos;
+ break;
+ case SEEK_CUR:
+ _pos += offset;
+ if (_pos >= 0 && size_t(_pos) >= _buffer_size()) {
+ _buf.resize(_pos + 1, '\0');
+ _update_ptrs();
+ }
+ *new_offset = _pos;
+ break;
+ case SEEK_END:
+ _pos = _buffer_size() ? _buffer_size() - 1 + offset : _buffer_size() + offset;
+ _buf.resize(_pos + 1, '\0');
+ _update_ptrs();
+ *new_offset = _pos;
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+void memstream_mem_file::_update_ptrs() {
+ *_bufloc = _buf.data();
+ *_sizeloc = _buf.size() - 1;
+}
+
+fmemopen_mem_file::fmemopen_mem_file(void *in_buf, size_t size, int flags, void (*do_dispose)(abstract_file *))
+: mem_file{flags, do_dispose}, _inBuffer{in_buf}, _inBufferSize{size} {
+ if(!_inBuffer) {
+ _inBuffer = getAllocator().allocate(size);
+ _needsDeallocation = true;
+ }
+
+ if(_flags & O_APPEND) {
+ // the initial seek-size for append is zero if buf was NULL, or the first '\0' found, or the size
+ _max_size = (_needsDeallocation) ? 0 : strnlen(reinterpret_cast<char *>(_inBuffer), _inBufferSize);
+ _pos = _max_size;
+ } else if((_flags & O_WRONLY || _flags & O_RDWR) && _flags & O_CREAT && _flags & O_TRUNC) {
+ // modes: "w", "w+"
+ _max_size = 0;
+ } else {
+ _max_size = size;
+ }
+}
+
+int fmemopen_mem_file::close() {
+ if(_needsDeallocation) {
+ getAllocator().free(_inBuffer);
+ }
+
+ return 0;
+}
+
+int fmemopen_mem_file::io_read(char *buffer, size_t max_size, size_t *actual_size) {
+ if ((_pos >= 0 && _pos >= _max_size) || !max_size) {
+ *actual_size = 0;
+ return 0;
+ }
+
+ size_t bytes_read = std::min(size_t(_max_size - _pos), max_size);
+ memcpy(buffer, _buffer().data() + _pos, bytes_read);
+ _pos += bytes_read;
+ *actual_size = bytes_read;
+ return 0;
+}
+
+int fmemopen_mem_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) {
+ off_t bytes_write = std::min(static_cast<size_t>(_buffer_size() - _pos), max_size);
+ memcpy(_buffer().data() + _pos, buffer, bytes_write);
+ _pos += bytes_write;
+ *actual_size = bytes_write;
+
+ if(_pos > _max_size) {
+ _max_size = _pos;
+ }
+
+ // upon flushing, we need to put a null byte at the current position or at the end of the buffer
+ size_t null = _pos;
+ // a special case is if the mode is set to updating ('+'), then it always goes at the end
+ if(null >= _buffer_size() || _flags & O_RDWR) {
+ null = _buffer_size() - 1;
+ }
+
+ if(_buffer_size()) {
+ _buffer()[null] = '\0';
+ }
+
+ return 0;
+}
+
+int fmemopen_mem_file::io_seek(off_t offset, int whence, off_t *new_offset) {
+ switch (whence) {
+ case SEEK_SET:
+ if(offset < 0 || size_t(offset) > _buffer_size()) {
+ return EINVAL;
+ }
+ _pos = offset;
+ *new_offset = _pos;
+ break;
+ case SEEK_CUR:
+ // seeking to negative positions or positions larger than the buffer is disallowed in fmemopen(3)
+ if((_pos + offset) < 0 || size_t(_pos + offset) > _buffer_size()) {
+ return EINVAL;
+ }
+ _pos += offset;
+ *new_offset = _pos;
+ break;
+ case SEEK_END:
+ if((_max_size + offset) < 0 || size_t(_max_size + offset) > _buffer_size()) {
+ return EINVAL;
+ }
+ _pos = _max_size + offset;
+ *new_offset = _pos;
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+int cookie_file::close() {
+ if(!_funcs.close) {
+ return 0;
+ }
+
+ return _funcs.close(_cookie);
+}
+
+int cookie_file::reopen(const char *, const char *) {
+ mlibc::panicLogger() << "mlibc: freopen() on a cookie_file stream is unimplemented!" << frg::endlog;
+ return -1;
+}
+
+int cookie_file::determine_type(stream_type *type) {
+ *type = stream_type::file_like;
+ return 0;
+}
+
+int cookie_file::determine_bufmode(buffer_mode *mode) {
+ *mode = buffer_mode::no_buffer;
+ return 0;
+}
+
+int cookie_file::io_read(char *buffer, size_t max_size, size_t *actual_size) {
+ if(!_funcs.read) {
+ return EOF;
+ }
+
+ *actual_size = _funcs.read(_cookie, buffer, max_size);
+
+ return 0;
+}
+
+int cookie_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) {
+ if(!_funcs.write) {
+ return 0;
+ }
+
+ *actual_size = _funcs.write(_cookie, buffer, max_size);
+
+ return 0;
+}
+
+int cookie_file::io_seek(off_t offset, int whence, off_t *new_offset) {
+ if(!_funcs.seek) {
+ return ENOTSUP;
+ }
+
+ *new_offset = offset;
+
+ return _funcs.seek(_cookie, new_offset, whence);
+}
+
+} // namespace mlibc
+
+FILE *fdopen(int fd, const char *mode) {
+ int flags = mlibc::fd_file::parse_modestring(mode);
+
+ flags &= ~O_TRUNC; // 'w' should not truncate the file
+
+ if (flags & O_APPEND) {
+ int cur_flags = fcntl(fd, F_GETFL, 0);
+ if (cur_flags < 0) {
+ errno = EINVAL;
+ return nullptr;
+ } else if (!(cur_flags & O_APPEND)) {
+ if (fcntl(fd, F_SETFL, cur_flags | O_APPEND)) {
+ errno = EINVAL;
+ return nullptr;
+ }
+ }
+ }
+
+ if (flags & O_CLOEXEC) {
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+ errno = EINVAL;
+ return nullptr;
+ }
+ }
+
+ // TODO: We may need to activate line buffered mode for terminals.
+
+ return frg::construct<mlibc::fd_file>(getAllocator(), fd,
+ mlibc::file_dispose_cb<mlibc::fd_file>);
+}
diff --git a/lib/mlibc/options/posix/generic/posix_ctype.cpp b/lib/mlibc/options/posix/generic/posix_ctype.cpp
new file mode 100644
index 0000000..19f129f
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_ctype.cpp
@@ -0,0 +1,136 @@
+#include <ctype.h>
+#include <wctype.h>
+
+#include <bits/ensure.h>
+
+int isalnum_l(int c, locale_t) {
+ return isalnum(c);
+}
+
+int isalpha_l(int c, locale_t) {
+ return isalpha(c);
+}
+
+int isblank_l(int c, locale_t) {
+ return isblank(c);
+}
+
+int iscntrl_l(int c, locale_t) {
+ return iscntrl(c);
+}
+
+int isdigit_l(int c, locale_t) {
+ return isdigit(c);
+}
+
+int isgraph_l(int c, locale_t) {
+ return isgraph(c);
+}
+
+int islower_l(int c, locale_t) {
+ return islower(c);
+}
+
+int isprint_l(int c, locale_t) {
+ return isprint(c);
+}
+
+int ispunct_l(int c, locale_t) {
+ return ispunct(c);
+}
+
+int isspace_l(int c, locale_t) {
+ return isspace(c);
+}
+
+int isupper_l(int c, locale_t) {
+ return isupper(c);
+}
+
+int isxdigit_l(int c, locale_t) {
+ return isxdigit(c);
+}
+
+int isascii_l(int c, locale_t) {
+ return isascii(c);
+}
+
+int tolower_l(int c, locale_t) {
+ return tolower(c);
+}
+
+int toupper_l(int c, locale_t) {
+ return toupper(c);
+}
+
+int iswalnum_l(wint_t c, locale_t) {
+ return iswalnum(c);
+}
+
+int iswblank_l(wint_t c, locale_t) {
+ return iswblank(c);
+}
+
+int iswcntrl_l(wint_t c, locale_t) {
+ return iswcntrl(c);
+}
+
+int iswdigit_l(wint_t c, locale_t) {
+ return iswdigit(c);
+}
+
+int iswgraph_l(wint_t c, locale_t) {
+ return iswgraph(c);
+}
+
+int iswlower_l(wint_t c, locale_t) {
+ return iswlower(c);
+}
+
+int iswprint_l(wint_t c, locale_t) {
+ return iswprint(c);
+}
+
+int iswpunct_l(wint_t c, locale_t) {
+ return iswpunct(c);
+}
+
+int iswspace_l(wint_t c, locale_t) {
+ return iswspace(c);
+}
+
+int iswupper_l(wint_t c, locale_t) {
+ return iswupper(c);
+}
+
+int iswxdigit_l(wint_t c, locale_t) {
+ return iswxdigit(c);
+}
+
+int iswalpha_l(wint_t c, locale_t) {
+ return iswalpha(c);
+}
+
+wctype_t wctype_l(const char* p, locale_t) {
+ return wctype(p);
+}
+
+int iswctype_l(wint_t w, wctype_t t, locale_t) {
+ return iswctype(w, t);
+}
+
+wint_t towlower_l(wint_t c, locale_t) {
+ return towlower(c);
+}
+
+wint_t towupper_l(wint_t c, locale_t) {
+ return towupper(c);
+}
+
+wctrans_t wctrans_l(const char* c, locale_t) {
+ return wctrans(c);
+}
+
+wint_t towctrans_l(wint_t c, wctrans_t desc, locale_t) {
+ return towctrans(c, desc);
+}
diff --git a/lib/mlibc/options/posix/generic/posix_locale.cpp b/lib/mlibc/options/posix/generic/posix_locale.cpp
new file mode 100644
index 0000000..bd8710a
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_locale.cpp
@@ -0,0 +1,37 @@
+#include <bits/posix/posix_locale.h>
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+namespace {
+
+bool newlocale_seen = false;
+bool uselocale_seen = false;
+
+}
+
+locale_t newlocale(int, const char *, locale_t) {
+ // Due to all of the locale functions being stubs, the locale will not be used
+ if(!newlocale_seen) {
+ mlibc::infoLogger() << "mlibc: newlocale() is a no-op" << frg::endlog;
+ newlocale_seen = true;
+ }
+ return nullptr;
+}
+
+void freelocale(locale_t) {
+ mlibc::infoLogger() << "mlibc: freelocale() is a no-op" << frg::endlog;
+ return;
+}
+
+locale_t uselocale(locale_t) {
+ if(!uselocale_seen) {
+ mlibc::infoLogger() << "mlibc: uselocale() is a no-op" << frg::endlog;
+ uselocale_seen = true;
+ }
+ return nullptr;
+}
+
+locale_t duplocale(locale_t) {
+ mlibc::infoLogger() << "mlibc: duplocale() is a no-op" << frg::endlog;
+ return nullptr;
+}
diff --git a/lib/mlibc/options/posix/generic/posix_signal.cpp b/lib/mlibc/options/posix/generic/posix_signal.cpp
new file mode 100644
index 0000000..eef3ef3
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_signal.cpp
@@ -0,0 +1,151 @@
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <bits/ensure.h>
+
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/tcb.hpp>
+
+int sigsuspend(const sigset_t *sigmask) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigsuspend, -1);
+
+ // This is guaranteed to return an error (EINTR most probably)
+ errno = mlibc::sys_sigsuspend(sigmask);
+ return -1;
+}
+
+int pthread_sigmask(int how, const sigset_t *__restrict set, sigset_t *__restrict retrieve) {
+ if(!mlibc::sys_sigprocmask) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+ if(int e = mlibc::sys_sigprocmask(how, set, retrieve); e) {
+ return e;
+ }
+ return 0;
+}
+
+int pthread_kill(pthread_t thread, int sig) {
+ auto tcb = reinterpret_cast<Tcb *>(thread);
+ auto pid = getpid();
+
+ if(!mlibc::sys_tgkill) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+
+ if(int e = mlibc::sys_tgkill(pid, tcb->tid, sig); e) {
+ return e;
+ }
+
+ return 0;
+}
+
+int sigaction(int signum, const struct sigaction *__restrict act, struct sigaction *__restrict oldact) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigaction, -1);
+ if(int e = mlibc::sys_sigaction(signum, act, oldact); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int siginterrupt(int sig, int flag) {
+ int ret;
+ struct sigaction act;
+
+ sigaction(sig, NULL, &act);
+ if (flag)
+ act.sa_flags &= ~SA_RESTART;
+ else
+ act.sa_flags |= SA_RESTART;
+
+ ret = sigaction(sig, &act, NULL);
+ return ret;
+}
+
+int kill(pid_t pid, int number) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_kill, -1);
+ if(int e = mlibc::sys_kill(pid, number); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int killpg(pid_t pgrp, int sig) {
+ if(pgrp > 1) {
+ return kill(-pgrp, sig);
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+int sigtimedwait(const sigset_t *__restrict set, siginfo_t *__restrict info, const struct timespec *__restrict timeout) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigtimedwait, -1);
+
+ int signo;
+
+ if (int e = sysdep(set, info, timeout, &signo)) {
+ errno = e;
+ return -1;
+ }
+
+ return signo;
+}
+
+int sigwaitinfo(const sigset_t *__restrict set, siginfo_t *__restrict info) {
+ // NOTE: This assumes the sysdep behavior noted in mlibc/posix-sysdeps.hpp
+ return sigtimedwait(set, info, nullptr);
+}
+
+int sigwait(const sigset_t *__restrict set, int *__restrict sig) {
+ if (int e = sigwaitinfo(set, nullptr); e < 0) {
+ return e;
+ } else {
+ if (sig)
+ *sig = e;
+
+ return 0;
+ }
+}
+
+int sigpending(sigset_t *set) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigpending, -1);
+
+ if(int e = sysdep(set)) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sigaltstack(const stack_t *__restrict ss, stack_t *__restrict oss) {
+ if (ss && ss->ss_size < MINSIGSTKSZ && !(ss->ss_flags & SS_DISABLE)) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sigaltstack, -1);
+ if (int e = mlibc::sys_sigaltstack(ss, oss); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+#if __MLIBC_GLIBC_OPTION
+int sigisemptyset(const sigset_t *set) {
+ return !(*set);
+}
+#endif // __MLIBC_GLIBC_OPTION
+
+int sigqueue(pid_t, int, const union sigval) {
+ __ensure(!"sigqueue() not implemented");
+ __builtin_unreachable();
+}
+
diff --git a/lib/mlibc/options/posix/generic/posix_stdio.cpp b/lib/mlibc/options/posix/generic/posix_stdio.cpp
new file mode 100644
index 0000000..fc77a54
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_stdio.cpp
@@ -0,0 +1,209 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/ansi-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/file-io.hpp>
+#include <mlibc/posix-file-io.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+struct popen_file : mlibc::fd_file {
+ popen_file(int fd, void (*do_dispose)(abstract_file *) = nullptr)
+ : fd_file(fd, do_dispose) {}
+
+ pid_t get_popen_pid() {
+ return _popen_pid;
+ }
+
+ void set_popen_pid(pid_t new_pid) {
+ _popen_pid = new_pid;
+ }
+
+private:
+ // Underlying PID in case of popen()
+ pid_t _popen_pid;
+};
+
+FILE *fmemopen(void *buf, size_t size, const char *__restrict mode) {
+ int flags = mlibc::fd_file::parse_modestring(mode);
+
+ return frg::construct<mlibc::fmemopen_mem_file>(getAllocator(), buf, size, flags,
+ mlibc::file_dispose_cb<mlibc::mem_file>);
+}
+
+int pclose(FILE *stream) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1);
+
+ auto file = static_cast<popen_file *>(stream);
+
+ int status;
+ pid_t pid = file->get_popen_pid();
+
+ fclose(file);
+
+ if (mlibc::sys_waitpid(pid, &status, 0, NULL, &pid) != 0) {
+ errno = ECHILD;
+ return -1;
+ }
+
+ return status;
+}
+
+FILE *popen(const char *command, const char *typestr) {
+ bool is_write;
+ pid_t child;
+ FILE *ret = nullptr;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork && mlibc::sys_dup2 && mlibc::sys_execve &&
+ mlibc::sys_sigprocmask && mlibc::sys_sigaction && mlibc::sys_pipe, nullptr);
+
+ if (typestr == NULL) {
+ errno = EINVAL;
+ return nullptr;
+ }
+
+ if (strstr(typestr, "w") != NULL) {
+ is_write = true;
+ } else if (strstr(typestr, "r") != NULL) {
+ is_write = false;
+ } else {
+ errno = EINVAL;
+ return nullptr;
+ }
+
+ bool cloexec = false;
+ if (strstr(typestr, "e") != NULL) {
+ // Set FD_CLOEXEC on the new file descriptor
+ cloexec = true;
+ }
+
+ int fds[2];
+ if (int e = mlibc::sys_pipe(fds, 0)) {
+ errno = e;
+ return nullptr;
+ }
+
+ struct sigaction new_sa, old_int, old_quit;
+ sigset_t new_mask, old_mask;
+
+ new_sa.sa_handler = SIG_IGN;
+ new_sa.sa_flags = 0;
+ sigemptyset(&new_sa.sa_mask);
+ mlibc::sys_sigaction(SIGINT, &new_sa, &old_int);
+ mlibc::sys_sigaction(SIGQUIT, &new_sa, &old_quit);
+
+ sigemptyset(&new_mask);
+ sigaddset(&new_mask, SIGCHLD);
+ mlibc::sys_sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
+
+ int parent_end = is_write ? 1 : 0;
+ int child_end = is_write ? 0 : 1;
+
+ if (int e = mlibc::sys_fork(&child)) {
+ errno = e;
+ mlibc::sys_close(fds[0]);
+ mlibc::sys_close(fds[1]);
+ } else if (!child) {
+ // For the child
+ mlibc::sys_sigaction(SIGINT, &old_int, nullptr);
+ mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr);
+ mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+
+ mlibc::sys_close(fds[parent_end]);
+
+ if (mlibc::sys_dup2(fds[child_end], 0, is_write ? 0 : 1)) {
+ __ensure(!"sys_dup2() failed in popen()");
+ }
+ mlibc::sys_close(fds[child_end]);
+
+ const char *args[] = {
+ "sh", "-c", command, nullptr
+ };
+
+ mlibc::sys_execve("/bin/sh", const_cast<char **>(args), environ);
+ _Exit(127);
+ } else {
+ // For the parent
+ mlibc::sys_close(fds[child_end]);
+
+ ret = frg::construct<popen_file>(
+ getAllocator(),
+ fds[parent_end],
+ mlibc::file_dispose_cb<popen_file>
+ );
+ __ensure(ret);
+
+ auto file = static_cast<popen_file *>(ret);
+
+ file->set_popen_pid(child);
+
+ if (cloexec == true) {
+ fcntl(file->fd(), F_SETFD, O_CLOEXEC);
+ }
+ }
+
+ mlibc::sys_sigaction(SIGINT, &old_int, nullptr);
+ mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr);
+ mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+
+ return ret;
+}
+
+FILE *open_memstream(char **buf, size_t *sizeloc) {
+ return frg::construct<mlibc::memstream_mem_file>(getAllocator(), buf, sizeloc, O_RDWR,
+ mlibc::file_dispose_cb<mlibc::mem_file>);
+}
+
+int fseeko(FILE *file_base, off_t offset, int whence) {
+ auto file = static_cast<mlibc::abstract_file *>(file_base);
+ if(int e = file->seek(offset, whence); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+off_t ftello(FILE *file_base) {
+ auto file = static_cast<mlibc::abstract_file *>(file_base);
+ off_t current_offset;
+ if(int e = file->tell(&current_offset); e) {
+ errno = e;
+ return -1;
+ }
+ return current_offset;
+}
+
+int dprintf(int fd, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = vdprintf(fd, format, args);
+ va_end(args);
+ return result;
+}
+
+int vdprintf(int fd, const char *format, __builtin_va_list args) {
+ mlibc::fd_file file{fd};
+ int ret = vfprintf(&file, format, args);
+ file.flush();
+ return ret;
+}
+
+char *fgetln(FILE *, size_t *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+FILE *fopencookie(void *cookie, const char *__restrict mode, cookie_io_functions_t funcs) {
+ int flags = mlibc::fd_file::parse_modestring(mode);
+
+ return frg::construct<mlibc::cookie_file>(getAllocator(), cookie, flags, funcs,
+ mlibc::file_dispose_cb<mlibc::cookie_file>);
+}
diff --git a/lib/mlibc/options/posix/generic/posix_stdlib.cpp b/lib/mlibc/options/posix/generic/posix_stdlib.cpp
new file mode 100644
index 0000000..4010998
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_stdlib.cpp
@@ -0,0 +1,513 @@
+
+#include <abi-bits/fcntl.h>
+#include <bits/ensure.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <frg/small_vector.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/rtdl-config.hpp>
+
+namespace {
+ constexpr bool debugPathResolution = false;
+}
+
+// Borrowed from musl
+static uint32_t init[] = {
+0x00000000,0x5851f42d,0xc0b18ccf,0xcbb5f646,
+0xc7033129,0x30705b04,0x20fd5db4,0x9a8b7f78,
+0x502959d8,0xab894868,0x6c0356a7,0x88cdb7ff,
+0xb477d43f,0x70a3a52b,0xa8e4baf1,0xfd8341fc,
+0x8ae16fd9,0x742d2f7a,0x0d1f0796,0x76035e09,
+0x40f7702c,0x6fa72ca5,0xaaa84157,0x58a0df74,
+0xc74a0364,0xae533cc4,0x04185faf,0x6de3b115,
+0x0cab8628,0xf043bfa4,0x398150e9,0x37521657};
+
+static int n = 31;
+static int i = 3;
+static int j = 0;
+static uint32_t *x = init + 1;
+
+
+static uint32_t lcg31(uint32_t x) {
+ return (1103515245 * x + 12345) & 0x7fffffff;
+}
+
+static uint64_t lcg64(uint64_t x) {
+ return 6364136223846793005ull * x + 1;
+}
+
+static void *savestate(void) {
+ x[-1] = (n << 16) | (i << 8) | j;
+ return x - 1;
+}
+
+static void loadstate(uint32_t *state) {
+ x = state + 1;
+ n = x[-1] >> 16;
+ i = (x[-1] >> 8) & 0xff;
+ j = x[-1] & 0xff;
+}
+
+long random(void) {
+ long k;
+
+ if(n == 0) {
+ k = x[0] = lcg31(x[0]);
+ return k;
+ }
+ x[i] += x[j];
+ k = x[i] >> 1;
+ if(++i == n)
+ i = 0;
+ if(++j == n)
+ j = 0;
+
+ return k;
+}
+
+double drand48(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void srand48(long int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// Borrowed from musl
+void srandom(unsigned int seed) {
+ int k;
+ uint64_t s = seed;
+
+ if(n == 0) {
+ x[0] = s;
+ return;
+ }
+ i = n == 31 || n == 7 ? 3 : 1;
+ j = 0;
+ for(k = 0; k < n; k++) {
+ s = lcg64(s);
+ x[k] = s >> 32;
+ }
+ // Make sure x contains at least one odd number
+ x[0] |= 1;
+}
+
+char *initstate(unsigned int seed, char *state, size_t size) {
+ void *old;
+
+ if(size < 8)
+ return 0;
+ old = savestate();
+ if(size < 32)
+ n = 0;
+ else if(size < 64)
+ n = 7;
+ else if(size < 128)
+ n = 15;
+ else if(size < 256)
+ n = 31;
+ else
+ n = 63;
+ x = (uint32_t *)state + 1;
+ srandom(seed);
+ savestate();
+ return (char *)old;
+}
+
+char *setstate(char *state) {
+ void *old;
+
+ old = savestate();
+ loadstate((uint32_t *)state);
+ return (char *)old;
+}
+
+// ----------------------------------------------------------------------------
+// Path handling.
+// ----------------------------------------------------------------------------
+
+
+int mkostemps(char *pattern, int suffixlen, int flags) {
+ auto n = strlen(pattern);
+ if(n < (6 + static_cast<size_t>(suffixlen))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ flags &= ~O_WRONLY;
+
+ for(size_t i = 0; i < 6; i++) {
+ if(pattern[n - (6 + suffixlen) + i] == 'X')
+ continue;
+ errno = EINVAL;
+ return -1;
+ }
+
+ // TODO: Do an exponential search.
+ for(size_t i = 0; i < 999999; i++) {
+ char sfx = pattern[n - suffixlen];
+ __ensure(sprintf(pattern + (n - (6 + suffixlen)), "%06zu", i) == 6);
+ pattern[n - suffixlen] = sfx;
+
+ int fd;
+ if(int e = mlibc::sys_open(pattern, O_RDWR | O_CREAT | O_EXCL | flags, S_IRUSR | S_IWUSR, &fd); !e) {
+ return fd;
+ }else if(e != EEXIST) {
+ errno = e;
+ return -1;
+ }
+ }
+
+ errno = EEXIST;
+ return -1;
+}
+
+int mkostemp(char *pattern, int flags) {
+ return mkostemps(pattern, flags, 0);
+}
+
+int mkstemp(char *path) {
+ return mkostemp(path, 0);
+}
+
+int mkstemps(char *pattern, int suffixlen) {
+ return mkostemps(pattern, suffixlen, 0);
+}
+
+char *mkdtemp(char *pattern) {
+ mlibc::infoLogger() << "mlibc mkdtemp(" << pattern << ") called" << frg::endlog;
+ auto n = strlen(pattern);
+ __ensure(n >= 6);
+ if(n < 6) {
+ errno = EINVAL;
+ return NULL;
+ }
+ for(size_t i = 0; i < 6; i++) {
+ if(pattern[n - 6 + i] == 'X')
+ continue;
+ errno = EINVAL;
+ return NULL;
+ }
+
+ // TODO: Do an exponential search.
+ for(size_t i = 0; i < 999999; i++) {
+ __ensure(sprintf(pattern + (n - 6), "%06zu", i) == 6);
+ if(int e = mlibc::sys_mkdir(pattern, S_IRWXU); !e) {
+ return pattern;
+ }else if(e != EEXIST) {
+ errno = e;
+ return NULL;
+ }
+ }
+
+ errno = EEXIST;
+ return NULL;
+}
+
+char *realpath(const char *path, char *out) {
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): Called on '" << path << "'" << frg::endlog;
+ frg::string_view path_view{path};
+
+ // In case of the root, the string only contains the null-terminator.
+ frg::small_vector<char, PATH_MAX, MemoryAllocator> resolv{getAllocator()};
+ size_t ps;
+
+ // If the path is relative, we have to preprend the working directory.
+ if(path[0] == '/') {
+ resolv.push_back(0);
+ ps = 1;
+ }else{
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getcwd, nullptr);
+
+ // Try to getcwd() until the buffer is large enough.
+ resolv.resize(128);
+ while(true) {
+ int e = mlibc::sys_getcwd(resolv.data(), resolv.size());
+ if(e == ERANGE) {
+ resolv.resize(2 * resolv.size());
+ }else if(!e) {
+ break;
+ }else{
+ errno = e;
+ return nullptr;
+ }
+ }
+ frg::string_view cwd_view{resolv.data()};
+ if(cwd_view == "/") {
+ // Restore our invariant that we only store the null-terminator for the root.
+ resolv.resize(1);
+ resolv[0] = 0;
+ }else{
+ resolv.resize(cwd_view.size() + 1);
+ }
+ ps = 0;
+ }
+
+ // Contains unresolved links as a relative path compared to resolv.
+ frg::small_vector<char, PATH_MAX, MemoryAllocator> lnk{getAllocator()};
+ size_t ls = 0;
+
+ auto process_segment = [&] (frg::string_view s_view) -> int {
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): resolv is '" << resolv.data() << "'"
+ << ", segment is " << s_view.data()
+ << ", size: " << s_view.size() << frg::endlog;
+
+ if(!s_view.size() || s_view == ".") {
+ // Keep resolv invariant.
+ return 0;
+ }else if(s_view == "..") {
+ // Remove a single segment from resolv.
+ if(resolv.size() > 1) {
+ auto slash = strrchr(resolv.data(), '/');
+ __ensure(slash); // We never remove the leading sla.
+ resolv.resize((slash - resolv.data()) + 1);
+ *slash = 0; // Replace the slash by a null-terminator.
+ }
+ return 0;
+ }
+
+ // Append the segment to resolv.
+ auto rsz = resolv.size();
+ resolv[rsz - 1] = '/'; // Replace null-terminator by a slash.
+ resolv.resize(rsz + s_view.size() + 1);
+ memcpy(resolv.data() + rsz, s_view.data(), s_view.size());
+ resolv[rsz + s_view.size()] = 0;
+
+ // stat() the path to (1) see if it exists and (2) see if it is a link.
+ if(!mlibc::sys_stat) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): stat()ing '"
+ << resolv.data() << "'" << frg::endlog;
+ struct stat st;
+ if(int e = mlibc::sys_stat(mlibc::fsfd_target::path,
+ -1, resolv.data(), AT_SYMLINK_NOFOLLOW, &st); e)
+ return e;
+
+ if(S_ISLNK(st.st_mode)) {
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Encountered symlink '"
+ << resolv.data() << "'" << frg::endlog;
+ }
+
+ if(!mlibc::sys_readlink) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+
+ ssize_t sz = 0;
+ char path[512];
+
+ if (int e = mlibc::sys_readlink(resolv.data(), path, 512, &sz); e)
+ return e;
+
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Symlink resolves to '"
+ << frg::string_view{path, static_cast<size_t>(sz)} << "'" << frg::endlog;
+ }
+
+ if (path[0] == '/') {
+ // Absolute path, replace resolv
+ resolv.resize(sz);
+ strncpy(resolv.data(), path, sz - 1);
+ resolv.data()[sz - 1] = 0;
+
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Symlink is absolute, resolv: '"
+ << resolv.data() << "'" << frg::endlog;
+ }
+ } else {
+ // Relative path, revert changes to resolv, prepend to lnk
+ resolv.resize(rsz);
+ resolv[rsz - 1] = 0;
+
+ auto lsz = lnk.size();
+ lnk.resize((lsz - ls) + sz + 1);
+ memmove(lnk.data() + sz, lnk.data() + ls, lsz - ls);
+ memcpy(lnk.data(), path, sz);
+ lnk[(lsz - ls) + sz] = 0;
+
+ ls = 0;
+
+ if(debugPathResolution) {
+ mlibc::infoLogger() << "mlibc realpath(): Symlink is relative, resolv: '"
+ << resolv.data() << "' lnk: '"
+ << frg::string_view{lnk.data(), lnk.size()} << "'" << frg::endlog;
+ }
+ }
+ }
+
+ return 0;
+ };
+
+ // Each iteration of this outer loop consumes segment of the input path.
+ // This design avoids copying the input path into lnk;
+ // the latter could often involve additional allocations.
+ while(ps < path_view.size()) {
+ frg::string_view ps_view;
+ if(auto slash = strchr(path + ps, '/'); slash) {
+ ps_view = frg::string_view{path + ps, static_cast<size_t>(slash - (path + ps))};
+ }else{
+ ps_view = frg::string_view{path + ps, strlen(path) - ps};
+ }
+ ps += ps_view.size() + 1;
+
+ // Handle one segment from the input path.
+ if(int e = process_segment(ps_view); e) {
+ errno = e;
+ return nullptr;
+ }
+
+ // This inner loop consumes segments of lnk.
+ while(ls < lnk.size()) {
+ frg::string_view ls_view;
+ if(auto slash = strchr(lnk.data() + ls, '/'); slash) {
+ ls_view = frg::string_view{lnk.data() + ls, static_cast<size_t>(slash - (lnk.data() + ls))};
+ }else{
+ ls_view = frg::string_view{lnk.data() + ls, strlen(lnk.data()) - ls};
+ }
+ ls += ls_view.size() + 1;
+
+ // Handle one segment from the link
+ if(int e = process_segment(ls_view); e) {
+ errno = e;
+ return nullptr;
+ }
+ }
+
+ // All of lnk was consumed, reset it
+ lnk.resize(0);
+ ls = 0;
+ }
+
+ if(resolv.size() == 1) {
+ resolv.resize(0);
+ resolv.push_back('/');
+ resolv.push_back(0);
+ }
+
+ if(debugPathResolution)
+ mlibc::infoLogger() << "mlibc realpath(): Returns '" << resolv.data() << "'" << frg::endlog;
+
+ if(resolv.size() > PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return nullptr;
+ }
+
+ if(!out)
+ out = reinterpret_cast<char *>(getAllocator().allocate(resolv.size()));
+ strcpy(out, resolv.data());
+ return out;
+}
+
+// ----------------------------------------------------------------------------
+// Pseudoterminals
+// ----------------------------------------------------------------------------
+
+int ptsname_r(int fd, char *buffer, size_t length) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ptsname, ENOSYS);
+
+ if(int e = sysdep(fd, buffer, length); e)
+ return e;
+
+ return 0;
+}
+
+char *ptsname(int fd) {
+ static char buffer[128];
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ptsname, NULL);
+
+ if(int e = sysdep(fd, buffer, 128); e) {
+ errno = e;
+ return NULL;
+ }
+
+ return buffer;
+}
+
+int posix_openpt(int flags) {
+ int fd;
+ if(int e = mlibc::sys_open("/dev/ptmx", flags, 0, &fd); e) {
+ errno = e;
+ return -1;
+ }
+
+ return fd;
+}
+
+int unlockpt(int fd) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlockpt, -1);
+
+ if(int e = sysdep(fd); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int grantpt(int) {
+ return 0;
+}
+
+double strtod_l(const char *__restrict__ nptr, char ** __restrict__ endptr, locale_t) {
+ mlibc::infoLogger() << "mlibc: strtod_l ignores locale!" << frg::endlog;
+ return strtod(nptr, endptr);
+}
+
+long double strtold_l(const char *__restrict__, char ** __restrict__, locale_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+float strtof_l(const char *__restrict__ nptr, char **__restrict__ endptr, locale_t) {
+ mlibc::infoLogger() << "mlibc: strtof_l ignores locales" << frg::endlog;
+ return strtof(nptr, endptr);
+}
+
+int strcoll_l(const char *, const char *, locale_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int getloadavg(double *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+char *secure_getenv(const char *name) {
+ if (mlibc::rtdlConfig().secureRequired)
+ return NULL;
+ else
+ return getenv(name);
+}
+
+void *reallocarray(void *ptr, size_t m, size_t n) {
+ if(n && m > -1 / n) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ return realloc(ptr, m * n);
+}
+
+char *canonicalize_file_name(const char *name) {
+ return realpath(name, NULL);
+}
diff --git a/lib/mlibc/options/posix/generic/posix_string.cpp b/lib/mlibc/options/posix/generic/posix_string.cpp
new file mode 100644
index 0000000..d0bc7b5
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_string.cpp
@@ -0,0 +1,174 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <bits/ensure.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <signal.h>
+
+#include <mlibc/debug.hpp>
+
+char *strdup(const char *string) {
+ auto num_bytes = strlen(string);
+
+ char *new_string = (char *)malloc(num_bytes + 1);
+ if(!new_string) // TODO: set errno
+ return nullptr;
+
+ memcpy(new_string, string, num_bytes);
+ new_string[num_bytes] = 0;
+ return new_string;
+}
+
+char *strndup(const char *string, size_t max_size) {
+ auto num_bytes = strnlen(string, max_size);
+ char *new_string = (char *)malloc(num_bytes + 1);
+ if(!new_string) // TODO: set errno
+ return nullptr;
+
+ memcpy(new_string, string, num_bytes);
+ new_string[num_bytes] = 0;
+ return new_string;
+}
+
+char *stpcpy(char *__restrict dest, const char *__restrict src) {
+ auto n = strlen(src);
+ memcpy(dest, src, n + 1);
+ return dest + n;
+}
+
+char *stpncpy(char *__restrict dest, const char *__restrict src, size_t n) {
+ size_t nulls, copied, srcLen = strlen(src);
+ if (n >= srcLen) {
+ nulls = n - srcLen;
+ copied = srcLen;
+ } else {
+ nulls = 0;
+ copied = n;
+ }
+
+ memcpy(dest, src, copied);
+ memset(dest + srcLen, 0, nulls);
+ return dest + n - nulls;
+}
+
+size_t strnlen(const char *s, size_t n) {
+ size_t len = 0;
+ while(len < n && s[len])
+ ++len;
+ return len;
+}
+
+char *strsep(char **m, const char *del) {
+ __ensure(m);
+
+ auto tok = *m;
+ if(!tok)
+ return nullptr;
+
+ // Replace the following delimiter by a null-terminator.
+ // After this loop: *p is null iff we reached the end of the string.
+ auto p = tok;
+ while(*p && !strchr(del, *p))
+ p++;
+
+ if(*p) {
+ *p = 0;
+ *m = p + 1;
+ }else{
+ *m = nullptr;
+ }
+ return tok;
+}
+
+char *strsignal(int sig) {
+ #define CASE_FOR(sigconst) case sigconst: s = #sigconst; break;
+ const char *s;
+ switch(sig) {
+ CASE_FOR(SIGABRT)
+ CASE_FOR(SIGFPE)
+ CASE_FOR(SIGILL)
+ CASE_FOR(SIGINT)
+ CASE_FOR(SIGSEGV)
+ CASE_FOR(SIGTERM)
+ CASE_FOR(SIGPROF)
+ CASE_FOR(SIGIO)
+ CASE_FOR(SIGPWR)
+ CASE_FOR(SIGALRM)
+ CASE_FOR(SIGBUS)
+ CASE_FOR(SIGCHLD)
+ CASE_FOR(SIGCONT)
+ CASE_FOR(SIGHUP)
+ CASE_FOR(SIGKILL)
+ CASE_FOR(SIGPIPE)
+ CASE_FOR(SIGQUIT)
+ CASE_FOR(SIGSTOP)
+ CASE_FOR(SIGTSTP)
+ CASE_FOR(SIGTTIN)
+ CASE_FOR(SIGTTOU)
+ CASE_FOR(SIGUSR1)
+ CASE_FOR(SIGUSR2)
+ CASE_FOR(SIGSYS)
+ CASE_FOR(SIGTRAP)
+ CASE_FOR(SIGURG)
+ CASE_FOR(SIGVTALRM)
+ CASE_FOR(SIGXCPU)
+ CASE_FOR(SIGXFSZ)
+ CASE_FOR(SIGWINCH)
+ default:
+ mlibc::infoLogger() << "mlibc: Unknown signal number " << sig << frg::endlog;
+ s = "Unknown signal number";
+ }
+ return const_cast<char *>(s);
+}
+
+char *strcasestr(const char *s, const char *pattern) {
+ size_t plen = strlen(pattern);
+ const char *p = s;
+ while(*p) {
+ // Need strncasecmp() to avoid checking past the end of a successful match.
+ if(!strncasecmp(p, pattern, plen))
+ return const_cast<char *>(p);
+ ++p;
+ }
+ return nullptr;
+}
+
+void *memccpy(void *__restrict, const void *__restrict, int, size_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// This implementation was taken from musl
+void *memrchr(const void *m, int c, size_t n) {
+ const unsigned char *s = (const unsigned char *)m;
+ c = (unsigned char)c;
+ while(n--) {
+ if(s[n] == c)
+ return (void *)(s + n);
+ }
+ return 0;
+}
+
+// BSD extensions.
+// Taken from musl
+size_t strlcpy(char *d, const char *s, size_t n) {
+ char *d0 = d;
+
+ if(!n--)
+ goto finish;
+ for(; n && (*d=*s); n--, s++, d++);
+ *d = 0;
+finish:
+ return d-d0 + strlen(s);
+}
+
+size_t strlcat(char *d, const char *s, size_t n) {
+ size_t l = strnlen(d, n);
+ if(l == n) {
+ return l + strlen(s);
+ }
+ return l + strlcpy(d + l, s, n - l);
+}
diff --git a/lib/mlibc/options/posix/generic/posix_time.cpp b/lib/mlibc/options/posix/generic/posix_time.cpp
new file mode 100644
index 0000000..d93ebbc
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_time.cpp
@@ -0,0 +1,32 @@
+#include <bits/posix/posix_time.h>
+#include <bits/ensure.h>
+#include <mlibc/posix-sysdeps.hpp>
+#include <errno.h>
+
+int utimes(const char *filename, const struct timeval times[2]) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1);
+ struct timespec time[2];
+ if(times == nullptr) {
+ time[0].tv_sec = UTIME_NOW;
+ time[0].tv_nsec = UTIME_NOW;
+ time[1].tv_sec = UTIME_NOW;
+ time[1].tv_nsec = UTIME_NOW;
+ } else {
+ time[0].tv_sec = times[0].tv_sec;
+ time[0].tv_nsec = times[0].tv_usec * 1000;
+ time[1].tv_sec = times[1].tv_sec;
+ time[1].tv_nsec = times[1].tv_usec * 1000;
+ }
+
+ if (int e = mlibc::sys_utimensat(AT_FDCWD, filename, time, 0); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int futimes(int, const struct timeval[2]) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/pthread-stubs.cpp b/lib/mlibc/options/posix/generic/pthread-stubs.cpp
new file mode 100644
index 0000000..9720bc2
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/pthread-stubs.cpp
@@ -0,0 +1,1426 @@
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <bits/ensure.h>
+#include <frg/allocation.hpp>
+#include <frg/array.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/thread.hpp>
+#include <mlibc/tcb.hpp>
+#include <mlibc/tid.hpp>
+#include <mlibc/threads.hpp>
+
+static bool enableTrace = false;
+
+struct ScopeTrace {
+ ScopeTrace(const char *file, int line, const char *function)
+ : _file(file), _line(line), _function(function) {
+ if(!enableTrace)
+ return;
+ mlibc::infoLogger() << "trace: Enter scope "
+ << _file << ":" << _line << " (in function "
+ << _function << ")" << frg::endlog;
+ }
+
+ ~ScopeTrace() {
+ if(!enableTrace)
+ return;
+ mlibc::infoLogger() << "trace: Exit scope" << frg::endlog;
+ }
+
+private:
+ const char *_file;
+ int _line;
+ const char *_function;
+};
+
+#define SCOPE_TRACE() ScopeTrace(__FILE__, __LINE__, __FUNCTION__)
+
+static constexpr unsigned int mutexRecursive = 1;
+static constexpr unsigned int mutexErrorCheck = 2;
+
+// TODO: either use uint32_t or determine the bit based on sizeof(int).
+static constexpr unsigned int mutex_owner_mask = (static_cast<uint32_t>(1) << 30) - 1;
+static constexpr unsigned int mutex_waiters_bit = static_cast<uint32_t>(1) << 31;
+
+// Only valid for the internal __mlibc_m mutex of wrlocks.
+static constexpr unsigned int mutex_excl_bit = static_cast<uint32_t>(1) << 30;
+
+static constexpr unsigned int rc_count_mask = (static_cast<uint32_t>(1) << 31) - 1;
+static constexpr unsigned int rc_waiters_bit = static_cast<uint32_t>(1) << 31;
+
+static constexpr size_t default_stacksize = 0x200000;
+static constexpr size_t default_guardsize = 4096;
+
+// ----------------------------------------------------------------------------
+// pthread_attr and pthread functions.
+// ----------------------------------------------------------------------------
+
+// pthread_attr functions.
+int pthread_attr_init(pthread_attr_t *attr) {
+ *attr = pthread_attr_t{};
+ attr->__mlibc_stacksize = default_stacksize;
+ attr->__mlibc_guardsize = default_guardsize;
+ attr->__mlibc_detachstate = PTHREAD_CREATE_JOINABLE;
+ return 0;
+}
+
+int pthread_attr_destroy(pthread_attr_t *) {
+ return 0;
+}
+
+int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
+ *detachstate = attr->__mlibc_detachstate;
+ return 0;
+}
+int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
+ if (detachstate != PTHREAD_CREATE_DETACHED &&
+ detachstate != PTHREAD_CREATE_JOINABLE)
+ return EINVAL;
+
+ attr->__mlibc_detachstate = detachstate;
+ return 0;
+}
+
+int pthread_attr_getstacksize(const pthread_attr_t *__restrict attr, size_t *__restrict stacksize) {
+ *stacksize = attr->__mlibc_stacksize;
+ return 0;
+}
+
+int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) {
+ if (stacksize < PTHREAD_STACK_MIN)
+ return EINVAL;
+ attr->__mlibc_stacksize = stacksize;
+ return 0;
+}
+
+int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) {
+ *stackaddr = attr->__mlibc_stackaddr;
+ return 0;
+}
+int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) {
+ attr->__mlibc_stackaddr = stackaddr;
+ return 0;
+}
+
+int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize) {
+ *stackaddr = attr->__mlibc_stackaddr;
+ *stacksize = attr->__mlibc_stacksize;
+ return 0;
+}
+int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize) {
+ if (stacksize < PTHREAD_STACK_MIN)
+ return EINVAL;
+ attr->__mlibc_stacksize = stacksize;
+ attr->__mlibc_stackaddr = stackaddr;
+ return 0;
+}
+
+int pthread_attr_getguardsize(const pthread_attr_t *__restrict attr, size_t *__restrict guardsize) {
+ *guardsize = attr->__mlibc_guardsize;
+ return 0;
+}
+int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) {
+ attr->__mlibc_guardsize = guardsize;
+ return 0;
+}
+
+int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) {
+ *scope = attr->__mlibc_scope;
+ return 0;
+}
+int pthread_attr_setscope(pthread_attr_t *attr, int scope) {
+ if (scope != PTHREAD_SCOPE_SYSTEM &&
+ scope != PTHREAD_SCOPE_PROCESS)
+ return EINVAL;
+ if (scope == PTHREAD_SCOPE_PROCESS)
+ return ENOTSUP;
+ attr->__mlibc_scope = scope;
+ return 0;
+}
+
+int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched) {
+ *inheritsched = attr->__mlibc_inheritsched;
+ return 0;
+}
+int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) {
+ if (inheritsched != PTHREAD_INHERIT_SCHED &&
+ inheritsched != PTHREAD_EXPLICIT_SCHED)
+ return EINVAL;
+ attr->__mlibc_inheritsched = inheritsched;
+ return 0;
+}
+
+int pthread_attr_getschedparam(const pthread_attr_t *__restrict attr, struct sched_param *__restrict schedparam) {
+ *schedparam = attr->__mlibc_schedparam;
+ return 0;
+}
+int pthread_attr_setschedparam(pthread_attr_t *__restrict attr, const struct sched_param *__restrict schedparam) {
+ // TODO: this is supposed to return EINVAL for when the schedparam doesn't make sense
+ // for the given schedpolicy.
+ attr->__mlibc_schedparam = *schedparam;
+ return 0;
+}
+
+int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict attr, int *__restrict policy) {
+ *policy = attr->__mlibc_schedpolicy;
+ return 0;
+}
+int pthread_attr_setschedpolicy(pthread_attr_t *__restrict attr, int policy) {
+ if (policy != SCHED_FIFO && policy != SCHED_RR &&
+ policy != SCHED_OTHER)
+ return EINVAL;
+ attr->__mlibc_schedpolicy = policy;
+ return 0;
+}
+
+#if __MLIBC_LINUX_OPTION
+int pthread_attr_getaffinity_np(const pthread_attr_t *__restrict attr,
+ size_t cpusetsize, cpu_set_t *__restrict cpusetp) {
+ if (!attr)
+ return EINVAL;
+
+ if (!attr->__mlibc_cpuset) {
+ memset(cpusetp, -1, cpusetsize);
+ return 0;
+ }
+
+ for (size_t cnt = cpusetsize; cnt < attr->__mlibc_cpusetsize; cnt++)
+ if (reinterpret_cast<char*>(attr->__mlibc_cpuset)[cnt] != '\0')
+ return ERANGE;
+
+ auto p = memcpy(cpusetp, attr->__mlibc_cpuset,
+ std::min(cpusetsize, attr->__mlibc_cpusetsize));
+ if (cpusetsize > attr->__mlibc_cpusetsize)
+ memset(p, '\0', cpusetsize - attr->__mlibc_cpusetsize);
+
+ return 0;
+}
+
+int pthread_attr_setaffinity_np(pthread_attr_t *__restrict attr,
+ size_t cpusetsize, const cpu_set_t *__restrict cpusetp) {
+ if (!attr)
+ return EINVAL;
+
+ if (!cpusetp || !cpusetsize) {
+ attr->__mlibc_cpuset = NULL;
+ attr->__mlibc_cpusetsize = 0;
+ return 0;
+ }
+
+ if (attr->__mlibc_cpusetsize != cpusetsize) {
+ auto newp = realloc(attr->__mlibc_cpuset, cpusetsize);
+ if (!newp)
+ return ENOMEM;
+
+ attr->__mlibc_cpuset = static_cast<cpu_set_t*>(newp);
+ attr->__mlibc_cpusetsize = cpusetsize;
+ }
+
+ memcpy(attr->__mlibc_cpuset, cpusetp, cpusetsize);
+ return 0;
+}
+
+int pthread_attr_getsigmask_np(const pthread_attr_t *__restrict attr,
+ sigset_t *__restrict sigmask) {
+ if (!attr)
+ return EINVAL;
+
+ if (!attr->__mlibc_sigmaskset) {
+ sigemptyset(sigmask);
+ return PTHREAD_ATTR_NO_SIGMASK_NP;
+ }
+
+ *sigmask = attr->__mlibc_sigmask;
+
+ return 0;
+}
+int pthread_attr_setsigmask_np(pthread_attr_t *__restrict attr,
+ const sigset_t *__restrict sigmask) {
+ if (!attr)
+ return EINVAL;
+
+ if (!sigmask) {
+ attr->__mlibc_sigmaskset = 0;
+ return 0;
+ }
+
+ attr->__mlibc_sigmask = *sigmask;
+ attr->__mlibc_sigmaskset = 1;
+
+ // Filter out internally used signals.
+ sigdelset(&attr->__mlibc_sigmask, SIGCANCEL);
+
+ return 0;
+}
+
+namespace {
+ void get_own_stackinfo(void **stack_addr, size_t *stack_size) {
+ auto fp = fopen("/proc/self/maps", "r");
+ if (!fp) {
+ mlibc::infoLogger() << "mlibc pthreads: /proc/self/maps does not exist! Producing incorrect"
+ " stack results!" << frg::endlog;
+ return;
+ }
+
+ char line[256];
+ auto sp = mlibc::get_sp();
+ while (fgets(line, 256, fp)) {
+ uintptr_t from, to;
+ if(sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &from, &to) != 2)
+ continue;
+ if (sp < to && sp > from) {
+ // We need to return the lowest byte of the stack.
+ *stack_addr = reinterpret_cast<void*>(from);
+ *stack_size = to - from;
+ fclose(fp);
+ return;
+ }
+ }
+
+ fclose(fp);
+ }
+}
+
+int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) {
+ auto tcb = reinterpret_cast<Tcb*>(thread);
+ *attr = pthread_attr_t{};
+
+ if (!tcb->stackAddr || !tcb->stackSize) {
+ get_own_stackinfo(&attr->__mlibc_stackaddr, &attr->__mlibc_stacksize);
+ } else {
+ attr->__mlibc_stacksize = tcb->stackSize;
+ attr->__mlibc_stackaddr = tcb->stackAddr;
+ }
+
+ attr->__mlibc_guardsize = tcb->guardSize;
+ attr->__mlibc_detachstate = tcb->isJoinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED;
+ mlibc::infoLogger() << "pthread_getattr_np(): Implementation is incomplete!" << frg::endlog;
+ return 0;
+}
+
+int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *mask) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getthreadaffinity, ENOSYS);
+ return mlibc::sys_getthreadaffinity(reinterpret_cast<Tcb*>(thread)->tid, cpusetsize, mask);
+}
+
+int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *mask) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setthreadaffinity, ENOSYS);
+ return mlibc::sys_setthreadaffinity(reinterpret_cast<Tcb*>(thread)->tid, cpusetsize, mask);
+}
+#endif // __MLIBC_LINUX_OPTION
+
+extern "C" Tcb *__rtdl_allocateTcb();
+
+// pthread functions.
+int pthread_create(pthread_t *__restrict thread, const pthread_attr_t *__restrict attrp,
+ void *(*entry) (void *), void *__restrict user_arg) {
+ return mlibc::thread_create(thread, attrp, reinterpret_cast<void *>(entry), user_arg, false);
+}
+
+pthread_t pthread_self(void) {
+ return reinterpret_cast<pthread_t>(mlibc::get_current_tcb());
+}
+
+int pthread_equal(pthread_t t1, pthread_t t2) {
+ if(t1 == t2)
+ return 1;
+ return 0;
+}
+
+namespace {
+ struct key_global_info {
+ bool in_use;
+
+ void (*dtor)(void *);
+ uint64_t generation;
+ };
+
+ constinit frg::array<
+ key_global_info,
+ PTHREAD_KEYS_MAX
+ > key_globals_{};
+
+ FutexLock key_mutex_;
+}
+
+namespace mlibc {
+ __attribute__ ((__noreturn__)) void do_exit() {
+ sys_thread_exit();
+ __builtin_unreachable();
+ }
+}
+
+__attribute__ ((__noreturn__)) void pthread_exit(void *ret_val) {
+ auto self = mlibc::get_current_tcb();
+
+ if (__atomic_load_n(&self->cancelBits, __ATOMIC_RELAXED) & tcbExitingBit)
+ mlibc::do_exit();
+
+ __atomic_fetch_or(&self->cancelBits, tcbExitingBit, __ATOMIC_RELAXED);
+
+ auto hand = self->cleanupEnd;
+ while (hand) {
+ auto old = hand;
+ hand->func(hand->arg);
+ hand = hand->prev;
+ frg::destruct(getAllocator(), old);
+ }
+
+ for (size_t j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
+ for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++) {
+ if (auto v = pthread_getspecific(i)) {
+ key_mutex_.lock();
+ auto dtor = key_globals_[i].dtor;
+ key_mutex_.unlock();
+
+ if (dtor) {
+ dtor(v);
+ (*self->localKeys)[i].value = nullptr;
+ }
+ }
+ }
+ }
+
+ self->returnValue.voidPtr = ret_val;
+ __atomic_store_n(&self->didExit, 1, __ATOMIC_RELEASE);
+ mlibc::sys_futex_wake(&self->didExit);
+
+ // TODO: clean up thread resources when we are detached.
+
+ // TODO: do exit(0) when we're the only thread instead
+ mlibc::do_exit();
+}
+
+int pthread_join(pthread_t thread, void **ret) {
+ return mlibc::thread_join(thread, ret);
+}
+
+int pthread_detach(pthread_t thread) {
+ auto tcb = reinterpret_cast<Tcb*>(thread);
+ if (!__atomic_load_n(&tcb->isJoinable, __ATOMIC_RELAXED))
+ return EINVAL;
+
+ int expected = 1;
+ if(!__atomic_compare_exchange_n(&tcb->isJoinable, &expected, 0, false, __ATOMIC_RELEASE,
+ __ATOMIC_RELAXED))
+ return EINVAL;
+
+ return 0;
+}
+
+void pthread_cleanup_push(void (*func) (void *), void *arg) {
+ auto self = mlibc::get_current_tcb();
+
+ auto hand = frg::construct<Tcb::CleanupHandler>(getAllocator());
+ hand->func = func;
+ hand->arg = arg;
+ hand->next = nullptr;
+ hand->prev = self->cleanupEnd;
+
+ if (self->cleanupEnd)
+ self->cleanupEnd->next = hand;
+
+ self->cleanupEnd = hand;
+
+ if (!self->cleanupBegin)
+ self->cleanupBegin = self->cleanupEnd;
+}
+
+void pthread_cleanup_pop(int execute) {
+ auto self = mlibc::get_current_tcb();
+
+ auto hand = self->cleanupEnd;
+
+ if (self->cleanupEnd)
+ self->cleanupEnd = self->cleanupEnd->prev;
+ if (self->cleanupEnd)
+ self->cleanupEnd->next = nullptr;
+
+ if (execute)
+ hand->func(hand->arg);
+
+ frg::destruct(getAllocator(), hand);
+}
+
+int pthread_setname_np(pthread_t thread, const char *name) {
+ auto tcb = reinterpret_cast<Tcb*>(thread);
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_thread_setname, ENOSYS);
+ if(int e = sysdep(tcb, name); e) {
+ return e;
+ }
+
+ return 0;
+}
+
+int pthread_getname_np(pthread_t thread, char *name, size_t size) {
+ auto tcb = reinterpret_cast<Tcb*>(thread);
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_thread_getname, ENOSYS);
+ if(int e = sysdep(tcb, name, size); e) {
+ return e;
+ }
+
+ return 0;
+}
+
+int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) {
+ auto tcb = reinterpret_cast<Tcb*>(thread);
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setschedparam, ENOSYS);
+ if(int e = mlibc::sys_setschedparam(tcb, policy, param); e) {
+ return e;
+ }
+
+ return 0;
+}
+
+int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param) {
+ auto tcb = reinterpret_cast<Tcb*>(thread);
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getschedparam, ENOSYS);
+ if(int e = mlibc::sys_getschedparam(tcb, policy, param); e) {
+ return e;
+ }
+
+ return 0;
+}
+
+//pthread cancel functions
+
+extern "C" void __mlibc_do_cancel() {
+ //TODO(geert): for now the same as pthread_exit()
+ pthread_exit(PTHREAD_CANCELED);
+}
+
+namespace {
+
+ void sigcancel_handler(int signal, siginfo_t *info, void *ucontext) {
+ ucontext_t *uctx = static_cast<ucontext_t*>(ucontext);
+ // The function could be called from other signals, or from another
+ // process, in which case we should do nothing.
+ if (signal != SIGCANCEL || info->si_pid != getpid() ||
+ info->si_code != SI_TKILL)
+ return;
+
+ auto tcb = reinterpret_cast<Tcb*>(mlibc::get_current_tcb());
+ int old_value = tcb->cancelBits;
+
+ /*
+ * When a thread is marked with deferred cancellation and performs a blocking syscall,
+ * the spec mandates that the syscall can get interrupted before it has caused any side
+ * effects (e.g. before a read() has read any bytes from disk). If the syscall has
+ * already caused side effects it should return its partial work, and set the program
+ * counter just after the syscall. If the syscall hasn't caused any side effects, it
+ * should fail with EINTR and set the program counter to the syscall instruction.
+ *
+ * cancellable_syscall:
+ * test whether_a_cancel_is_queued
+ * je cancel
+ * syscall
+ * end_cancellable_syscall
+ *
+ * The mlibc::sys_before_cancellable_syscall sysdep should return 1 when the
+ * program counter is between the 'canellable_syscall' and 'end_cancellable_syscall' label.
+ */
+ if (!(old_value & tcbCancelAsyncBit) &&
+ mlibc::sys_before_cancellable_syscall && !mlibc::sys_before_cancellable_syscall(uctx))
+ return;
+
+ int bitmask = tcbCancelTriggerBit | tcbCancelingBit;
+ while (1) {
+ int new_value = old_value | bitmask;
+
+ // Check if we are already cancelled or exiting
+ if (old_value == new_value || old_value & tcbExitingBit)
+ return;
+
+ int current_value = old_value;
+ if (__atomic_compare_exchange_n(&tcb->cancelBits, &current_value,
+ new_value, true,__ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
+ tcb->returnValue.voidPtr = PTHREAD_CANCELED;
+
+ // Perform cancellation
+ __mlibc_do_cancel();
+
+ break;
+ }
+
+ old_value = current_value;
+ }
+ }
+}
+
+namespace mlibc {
+namespace {
+
+struct PthreadSignalInstaller {
+ PthreadSignalInstaller() {
+ struct sigaction sa;
+ sa.sa_sigaction = sigcancel_handler;
+ sa.sa_flags = SA_SIGINFO;
+ auto e = ENOSYS;
+ if(sys_sigaction)
+ e = sys_sigaction(SIGCANCEL, &sa, NULL);
+ // Opt-out of cancellation support.
+ if(e == ENOSYS)
+ return;
+ __ensure(!e);
+ }
+};
+
+PthreadSignalInstaller pthread_signal_installer;
+
+} // anonymous namespace
+} // namespace mlibc
+
+int pthread_setcanceltype(int type, int *oldtype) {
+ if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS)
+ return EINVAL;
+
+ auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb());
+ int old_value = self->cancelBits;
+ while (1) {
+ int new_value = old_value & ~tcbCancelAsyncBit;
+ if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
+ new_value |= tcbCancelAsyncBit;
+
+ if (oldtype)
+ *oldtype = ((old_value & tcbCancelAsyncBit)
+ ? PTHREAD_CANCEL_ASYNCHRONOUS
+ : PTHREAD_CANCEL_DEFERRED);
+
+ // Avoid unecessary atomic op.
+ if (old_value == new_value)
+ break;
+
+ int current_value = old_value;
+ if (__atomic_compare_exchange_n(&self->cancelBits, &current_value,
+ new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
+
+ if (mlibc::tcb_async_cancelled(new_value))
+ __mlibc_do_cancel();
+
+ break;
+ }
+
+ old_value = current_value;
+ }
+
+ return 0;
+}
+int pthread_setcancelstate(int state, int *oldstate) {
+ if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE)
+ return EINVAL;
+
+ auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb());
+ int old_value = self->cancelBits;
+ while (1) {
+ int new_value = old_value & ~tcbCancelEnableBit;
+ if (state == PTHREAD_CANCEL_ENABLE)
+ new_value |= tcbCancelEnableBit;
+
+ if (oldstate)
+ *oldstate = ((old_value & tcbCancelEnableBit)
+ ? PTHREAD_CANCEL_ENABLE
+ : PTHREAD_CANCEL_DISABLE);
+
+ // Avoid unecessary atomic op.
+ if (old_value == new_value)
+ break;
+
+ int current_value = old_value;
+ if (__atomic_compare_exchange_n(&self->cancelBits, &current_value,
+ new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
+
+ if (mlibc::tcb_async_cancelled(new_value))
+ __mlibc_do_cancel();
+
+ sigset_t set = {};
+ sigaddset(&set, SIGCANCEL);
+ if (new_value & PTHREAD_CANCEL_ENABLE)
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ else
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ break;
+ }
+
+ old_value = current_value;
+ }
+
+ return 0;
+}
+void pthread_testcancel(void) {
+ auto self = reinterpret_cast<Tcb *>(mlibc::get_current_tcb());
+ int value = self->cancelBits;
+ if ((value & tcbCancelEnableBit) && (value & tcbCancelTriggerBit)) {
+ __mlibc_do_cancel();
+ __builtin_unreachable();
+ }
+}
+int pthread_cancel(pthread_t thread) {
+ if (!mlibc::sys_tgkill) {
+ MLIBC_MISSING_SYSDEP();
+ return ENOSYS;
+ }
+
+ auto tcb = reinterpret_cast<Tcb *>(thread);
+ // Check if the TCB is valid, somewhat..
+ if (tcb->selfPointer != tcb)
+ return ESRCH;
+
+ int old_value = __atomic_load_n(&tcb->cancelBits, __ATOMIC_RELAXED);
+ while (1) {
+ int bitmask = tcbCancelTriggerBit;
+
+ int new_value = old_value | bitmask;
+ if (old_value == new_value)
+ break;
+
+ int current_value = old_value;
+ if (__atomic_compare_exchange_n(&tcb->cancelBits, &current_value,
+ new_value, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
+ if (mlibc::tcb_cancel_enabled(new_value)) {
+ pid_t pid = getpid();
+
+ int res = mlibc::sys_tgkill(pid, tcb->tid, SIGCANCEL);
+
+ current_value = __atomic_load_n(&tcb->cancelBits, __ATOMIC_RELAXED);
+
+ // If we can't find the thread anymore, it's possible that it exited between
+ // us setting the cancel trigger bit, and us sending the signal. Check the
+ // cancelBits for tcbExitingBit to confirm that.
+ // XXX(qookie): This will be an use-after-free once we start freeing TCBs on
+ // exit. Perhaps the TCB should be refcounted.
+ if (!(res == ESRCH && (current_value & tcbExitingBit)))
+ return res;
+ }
+
+ break;
+ }
+
+ old_value = current_value;
+ }
+
+ return 0;
+}
+
+int pthread_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void)) {
+ auto self = mlibc::get_current_tcb();
+
+ auto hand = frg::construct<Tcb::AtforkHandler>(getAllocator());
+ if (!hand)
+ return -1;
+
+ hand->prepare = prepare;
+ hand->parent = parent;
+ hand->child = child;
+ hand->next = nullptr;
+ hand->prev = self->atforkEnd;
+
+ if (self->atforkEnd)
+ self->atforkEnd->next = hand;
+
+ self->atforkEnd = hand;
+
+ if (!self->atforkBegin)
+ self->atforkBegin = self->atforkEnd;
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// pthread_key functions.
+// ----------------------------------------------------------------------------
+
+int pthread_key_create(pthread_key_t *out, void (*destructor)(void *)) {
+ SCOPE_TRACE();
+
+ auto g = frg::guard(&key_mutex_);
+
+ pthread_key_t key = PTHREAD_KEYS_MAX;
+ for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++) {
+ if (!key_globals_[i].in_use) {
+ key = i;
+ break;
+ }
+ }
+
+ if (key == PTHREAD_KEYS_MAX)
+ return EAGAIN;
+
+ key_globals_[key].in_use = true;
+ key_globals_[key].dtor = destructor;
+
+ *out = key;
+
+ return 0;
+}
+
+int pthread_key_delete(pthread_key_t key) {
+ SCOPE_TRACE();
+
+ auto g = frg::guard(&key_mutex_);
+
+ if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use)
+ return EINVAL;
+
+ key_globals_[key].in_use = false;
+ key_globals_[key].dtor = nullptr;
+ key_globals_[key].generation++;
+
+ return 0;
+}
+
+void *pthread_getspecific(pthread_key_t key) {
+ SCOPE_TRACE();
+
+ auto self = mlibc::get_current_tcb();
+ auto g = frg::guard(&key_mutex_);
+
+ if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use)
+ return nullptr;
+
+ if (key_globals_[key].generation > (*self->localKeys)[key].generation) {
+ (*self->localKeys)[key].value = nullptr;
+ (*self->localKeys)[key].generation = key_globals_[key].generation;
+ }
+
+ return (*self->localKeys)[key].value;
+}
+
+int pthread_setspecific(pthread_key_t key, const void *value) {
+ SCOPE_TRACE();
+
+ auto self = mlibc::get_current_tcb();
+ auto g = frg::guard(&key_mutex_);
+
+ if (key >= PTHREAD_KEYS_MAX || !key_globals_[key].in_use)
+ return EINVAL;
+
+ (*self->localKeys)[key].value = const_cast<void *>(value);
+ (*self->localKeys)[key].generation = key_globals_[key].generation;
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// pthread_once functions.
+// ----------------------------------------------------------------------------
+
+static constexpr unsigned int onceComplete = 1;
+static constexpr unsigned int onceLocked = 2;
+
+int pthread_once(pthread_once_t *once, void (*function) (void)) {
+ SCOPE_TRACE();
+
+ auto expected = __atomic_load_n(&once->__mlibc_done, __ATOMIC_ACQUIRE);
+
+ // fast path: the function was already run.
+ while(!(expected & onceComplete)) {
+ if(!expected) {
+ // try to acquire the mutex.
+ if(!__atomic_compare_exchange_n(&once->__mlibc_done,
+ &expected, onceLocked, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE))
+ continue;
+
+ function();
+
+ // unlock the mutex.
+ __atomic_exchange_n(&once->__mlibc_done, onceComplete, __ATOMIC_RELEASE);
+ if(int e = mlibc::sys_futex_wake((int *)&once->__mlibc_done); e)
+ __ensure(!"sys_futex_wake() failed");
+ return 0;
+ }else{
+ // a different thread is currently running the initializer.
+ __ensure(expected == onceLocked);
+ if(int e = mlibc::sys_futex_wait((int *)&once->__mlibc_done, onceLocked, nullptr); e)
+ __ensure(!"sys_futex_wait() failed");
+ expected = __atomic_load_n(&once->__mlibc_done, __ATOMIC_ACQUIRE);
+ }
+ }
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// pthread_mutexattr and pthread_mutex functions.
+// ----------------------------------------------------------------------------
+
+// pthread_mutexattr functions
+int pthread_mutexattr_init(pthread_mutexattr_t *attr) {
+ SCOPE_TRACE();
+ return mlibc::thread_mutexattr_init(attr);
+}
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) {
+ SCOPE_TRACE();
+ return mlibc::thread_mutexattr_destroy(attr);
+}
+
+int pthread_mutexattr_gettype(const pthread_mutexattr_t *__restrict attr, int *__restrict type) {
+ return mlibc::thread_mutexattr_gettype(attr, type);
+}
+
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) {
+ return mlibc::thread_mutexattr_settype(attr, type);
+}
+
+int pthread_mutexattr_getrobust(const pthread_mutexattr_t *__restrict attr,
+ int *__restrict robust) {
+ *robust = attr->__mlibc_robust;
+ return 0;
+}
+int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr, int robust) {
+ if (robust != PTHREAD_MUTEX_STALLED && robust != PTHREAD_MUTEX_ROBUST)
+ return EINVAL;
+
+ attr->__mlibc_robust = robust;
+ return 0;
+}
+
+int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared) {
+ *pshared = attr->__mlibc_pshared;
+ return 0;
+}
+int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) {
+ if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
+ return EINVAL;
+
+ attr->__mlibc_pshared = pshared;
+ return 0;
+}
+
+int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *__restrict attr,
+ int *__restrict protocol) {
+ *protocol = attr->__mlibc_protocol;
+ return 0;
+}
+
+int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol) {
+ if (protocol != PTHREAD_PRIO_NONE && protocol != PTHREAD_PRIO_INHERIT
+ && protocol != PTHREAD_PRIO_PROTECT)
+ return EINVAL;
+
+ attr->__mlibc_protocol = protocol;
+ return 0;
+}
+
+int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *__restrict attr,
+ int *__restrict prioceiling) {
+ (void)attr;
+ (void)prioceiling;
+ return EINVAL;
+}
+
+int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling) {
+ (void)attr;
+ (void)prioceiling;
+ return EINVAL;
+}
+
+// pthread_mutex functions
+int pthread_mutex_init(pthread_mutex_t *__restrict mutex,
+ const pthread_mutexattr_t *__restrict attr) {
+ SCOPE_TRACE();
+
+ return mlibc::thread_mutex_init(mutex, attr);
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex) {
+ return mlibc::thread_mutex_destroy(mutex);
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex) {
+ SCOPE_TRACE();
+
+ return mlibc::thread_mutex_lock(mutex);
+}
+
+int pthread_mutex_trylock(pthread_mutex_t *mutex) {
+ SCOPE_TRACE();
+
+ unsigned int this_tid = mlibc::this_tid();
+ unsigned int expected = __atomic_load_n(&mutex->__mlibc_state, __ATOMIC_RELAXED);
+ if(!expected) {
+ // Try to take the mutex here.
+ if(__atomic_compare_exchange_n(&mutex->__mlibc_state,
+ &expected, this_tid, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
+ __ensure(!mutex->__mlibc_recursion);
+ mutex->__mlibc_recursion = 1;
+ return 0;
+ }
+ } else {
+ // If this (recursive) mutex is already owned by us, increment the recursion level.
+ if((expected & mutex_owner_mask) == this_tid) {
+ if(!(mutex->__mlibc_flags & mutexRecursive)) {
+ return EBUSY;
+ }
+ ++mutex->__mlibc_recursion;
+ return 0;
+ }
+ }
+
+ return EBUSY;
+}
+
+int pthread_mutex_timedlock(pthread_mutex_t *__restrict,
+ const struct timespec *__restrict) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex) {
+ SCOPE_TRACE();
+
+ return mlibc::thread_mutex_unlock(mutex);
+}
+
+int pthread_mutex_consistent(pthread_mutex_t *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// ----------------------------------------------------------------------------
+// pthread_condattr and pthread_cond functions.
+// ----------------------------------------------------------------------------
+
+int pthread_condattr_init(pthread_condattr_t *attr) {
+ attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE;
+ attr->__mlibc_clock = CLOCK_REALTIME;
+ return 0;
+}
+
+int pthread_condattr_destroy(pthread_condattr_t *attr) {
+ memset(attr, 0, sizeof(*attr));
+ return 0;
+}
+
+int pthread_condattr_getclock(const pthread_condattr_t *__restrict attr,
+ clockid_t *__restrict clock) {
+ *clock = attr->__mlibc_clock;
+ return 0;
+}
+
+int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock) {
+ if (clock != CLOCK_REALTIME && clock != CLOCK_MONOTONIC
+ && clock != CLOCK_MONOTONIC_RAW && clock != CLOCK_REALTIME_COARSE
+ && clock != CLOCK_MONOTONIC_COARSE && clock != CLOCK_BOOTTIME)
+ return EINVAL;
+
+ attr->__mlibc_clock = clock;
+ return 0;
+}
+
+int pthread_condattr_getpshared(const pthread_condattr_t *__restrict attr,
+ int *__restrict pshared) {
+ *pshared = attr->__mlibc_pshared;
+ return 0;
+}
+
+int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
+ if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
+ return EINVAL;
+
+ attr->__mlibc_pshared = pshared;
+ return 0;
+}
+
+int pthread_cond_init(pthread_cond_t *__restrict cond, const pthread_condattr_t *__restrict attr) {
+ SCOPE_TRACE();
+
+ return mlibc::thread_cond_init(cond, attr);
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond) {
+ SCOPE_TRACE();
+
+ return mlibc::thread_cond_destroy(cond);
+}
+
+int pthread_cond_wait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex) {
+ return pthread_cond_timedwait(cond, mutex, nullptr);
+}
+
+int pthread_cond_timedwait(pthread_cond_t *__restrict cond, pthread_mutex_t *__restrict mutex,
+ const struct timespec *__restrict abstime) {
+ return mlibc::thread_cond_timedwait(cond, mutex, abstime);
+}
+
+int pthread_cond_signal(pthread_cond_t *cond) {
+ SCOPE_TRACE();
+
+ return pthread_cond_broadcast(cond);
+}
+
+int pthread_cond_broadcast(pthread_cond_t *cond) {
+ SCOPE_TRACE();
+
+ return mlibc::thread_cond_broadcast(cond);
+}
+
+// ----------------------------------------------------------------------------
+// pthread_barrierattr and pthread_barrier functions.
+// ----------------------------------------------------------------------------
+
+int pthread_barrierattr_init(pthread_barrierattr_t *attr) {
+ attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE;
+ return 0;
+}
+
+int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict attr,
+ int *__restrict pshared) {
+ *pshared = attr->__mlibc_pshared;
+ return 0;
+}
+
+int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) {
+ if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+
+ attr->__mlibc_pshared = pshared;
+ return 0;
+}
+
+int pthread_barrierattr_destroy(pthread_barrierattr_t *) {
+ return 0;
+}
+
+int pthread_barrier_init(pthread_barrier_t *__restrict barrier,
+ const pthread_barrierattr_t *__restrict attr, unsigned count) {
+ if (count == 0)
+ return EINVAL;
+
+ barrier->__mlibc_waiting = 0;
+ barrier->__mlibc_inside = 0;
+ barrier->__mlibc_seq = 0;
+ barrier->__mlibc_count = count;
+
+ // Since we don't implement these yet, set a flag to error later.
+ auto pshared = attr ? attr->__mlibc_pshared : PTHREAD_PROCESS_PRIVATE;
+ barrier->__mlibc_flags = pshared;
+
+ return 0;
+}
+
+int pthread_barrier_destroy(pthread_barrier_t *barrier) {
+ // Wait until there are no threads still using the barrier.
+ unsigned inside = 0;
+ do {
+ unsigned expected = __atomic_load_n(&barrier->__mlibc_inside, __ATOMIC_RELAXED);
+ if (expected == 0)
+ break;
+
+ int e = mlibc::sys_futex_wait((int *)&barrier->__mlibc_inside, expected, nullptr);
+ if (e != 0 && e != EAGAIN && e != EINTR)
+ mlibc::panicLogger() << "mlibc: sys_futex_wait() returned error " << e << frg::endlog;
+ } while (inside > 0);
+
+ memset(barrier, 0, sizeof *barrier);
+ return 0;
+}
+
+int pthread_barrier_wait(pthread_barrier_t *barrier) {
+ if (barrier->__mlibc_flags != 0) {
+ mlibc::panicLogger() << "mlibc: pthread_barrier_t flags were non-zero"
+ << frg::endlog;
+ }
+
+ // inside is incremented on entry and decremented on exit.
+ // This is used to synchronise with pthread_barrier_destroy, to ensure that a thread doesn't pass
+ // the barrier and immediately destroy its state while other threads still rely on it.
+
+ __atomic_fetch_add(&barrier->__mlibc_inside, 1, __ATOMIC_ACQUIRE);
+
+ auto leave = [&](){
+ unsigned inside = __atomic_sub_fetch(&barrier->__mlibc_inside, 1, __ATOMIC_RELEASE);
+ if (inside == 0)
+ mlibc::sys_futex_wake((int *)&barrier->__mlibc_inside);
+ };
+
+ unsigned seq = __atomic_load_n(&barrier->__mlibc_seq, __ATOMIC_ACQUIRE);
+
+ while (true) {
+ unsigned expected = __atomic_load_n(&barrier->__mlibc_waiting, __ATOMIC_RELAXED);
+ bool swapped = __atomic_compare_exchange_n(&barrier->__mlibc_waiting, &expected, expected + 1, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
+
+ if (swapped) {
+ if (expected + 1 == barrier->__mlibc_count) {
+ // We were the last thread to hit the barrier. Reset waiters and wake the others.
+ __atomic_fetch_add(&barrier->__mlibc_seq, 1, __ATOMIC_ACQUIRE);
+ __atomic_store_n(&barrier->__mlibc_waiting, 0, __ATOMIC_RELEASE);
+
+ mlibc::sys_futex_wake((int *)&barrier->__mlibc_seq);
+
+ leave();
+ return PTHREAD_BARRIER_SERIAL_THREAD;
+ }
+
+ while (true) {
+ int e = mlibc::sys_futex_wait((int *)&barrier->__mlibc_seq, seq, nullptr);
+ if (e != 0 && e != EAGAIN && e != EINTR)
+ mlibc::panicLogger() << "mlibc: sys_futex_wait() returned error " << e << frg::endlog;
+
+ unsigned newSeq = __atomic_load_n(&barrier->__mlibc_seq, __ATOMIC_ACQUIRE);
+ if (newSeq > seq) {
+ leave();
+ return 0;
+ }
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// pthread_rwlock functions.
+// ----------------------------------------------------------------------------
+
+namespace {
+ void rwlock_m_lock(pthread_rwlock_t *rw, bool excl) {
+ unsigned int m_expected = __atomic_load_n(&rw->__mlibc_m, __ATOMIC_RELAXED);
+ while(true) {
+ if(m_expected) {
+ __ensure(m_expected & mutex_owner_mask);
+
+ // Try to set the waiters bit.
+ if(!(m_expected & mutex_waiters_bit)) {
+ unsigned int desired = m_expected | mutex_waiters_bit;
+ if(!__atomic_compare_exchange_n(&rw->__mlibc_m,
+ &m_expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ continue;
+ }
+
+ // Wait on the futex.
+ mlibc::sys_futex_wait((int *)&rw->__mlibc_m, m_expected | mutex_waiters_bit, nullptr);
+
+ // Opportunistically try to take the lock after we wake up.
+ m_expected = 0;
+ }else{
+ // Try to lock the mutex.
+ unsigned int desired = 1;
+ if(excl)
+ desired |= mutex_excl_bit;
+ if(__atomic_compare_exchange_n(&rw->__mlibc_m,
+ &m_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+ break;
+ }
+ }
+ }
+
+ int rwlock_m_trylock(pthread_rwlock_t *rw, bool excl) {
+ unsigned int m_expected = __atomic_load_n(&rw->__mlibc_m, __ATOMIC_RELAXED);
+ if(!m_expected) {
+ // Try to lock the mutex.
+ unsigned int desired = 1;
+ if(excl)
+ desired |= mutex_excl_bit;
+ if(__atomic_compare_exchange_n(&rw->__mlibc_m,
+ &m_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+ return 0;
+ }
+
+ __ensure(m_expected & mutex_owner_mask);
+
+ // POSIX says that this function should never block but also that
+ // readers should not be blocked by readers. We implement this by returning EAGAIN
+ // (and not EBUSY) if a reader would block a reader.
+ if(!excl && !(m_expected & mutex_excl_bit))
+ return EAGAIN;
+
+ return EBUSY;
+ }
+
+ void rwlock_m_unlock(pthread_rwlock_t *rw) {
+ auto m = __atomic_exchange_n(&rw->__mlibc_m, 0, __ATOMIC_RELEASE);
+ if(m & mutex_waiters_bit)
+ mlibc::sys_futex_wake((int *)&rw->__mlibc_m);
+ }
+}
+
+int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) {
+ attr->__mlibc_pshared = PTHREAD_PROCESS_PRIVATE;
+ return 0;
+}
+
+int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *__restrict attr,
+ int *__restrict pshared) {
+ *pshared = attr->__mlibc_pshared;
+ return 0;
+}
+
+int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) {
+ if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+
+ attr->__mlibc_pshared = pshared;
+ return 0;
+}
+
+int pthread_rwlockattr_destroy(pthread_rwlockattr_t *) {
+ return 0;
+}
+
+int pthread_rwlock_init(pthread_rwlock_t *__restrict rw, const pthread_rwlockattr_t *__restrict attr) {
+ SCOPE_TRACE();
+ rw->__mlibc_m = 0;
+ rw->__mlibc_rc = 0;
+
+ // Since we don't implement this yet, set a flag to error later.
+ auto pshared = attr ? attr->__mlibc_pshared : PTHREAD_PROCESS_PRIVATE;
+ rw->__mlibc_flags = pshared;
+ return 0;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t *rw) {
+ __ensure(!rw->__mlibc_m);
+ __ensure(!rw->__mlibc_rc);
+ return 0;
+}
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rw) {
+ SCOPE_TRACE();
+
+ if (rw->__mlibc_flags != 0) {
+ mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
+ << frg::endlog;
+ }
+
+ // Take the __mlibc_m mutex.
+ // Will be released in pthread_rwlock_unlock().
+ if(int e = rwlock_m_trylock(rw, true))
+ return e;
+
+ // Check that there are no readers.
+ unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE);
+ if(rc_expected) {
+ rwlock_m_unlock(rw);
+ return EBUSY;
+ }
+
+ return 0;
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *rw) {
+ SCOPE_TRACE();
+
+ if (rw->__mlibc_flags != 0) {
+ mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
+ << frg::endlog;
+ }
+
+ // Take the __mlibc_m mutex.
+ // Will be released in pthread_rwlock_unlock().
+ rwlock_m_lock(rw, true);
+
+ // Now wait until there are no more readers.
+ unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE);
+ while(true) {
+ if(!rc_expected)
+ break;
+
+ __ensure(rc_expected & rc_count_mask);
+
+ // Try to set the waiters bit.
+ if(!(rc_expected & rc_waiters_bit)) {
+ unsigned int desired = rc_expected | rc_count_mask;
+ if(!__atomic_compare_exchange_n(&rw->__mlibc_rc,
+ &rc_expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE))
+ continue;
+ }
+
+ // Wait on the futex.
+ mlibc::sys_futex_wait((int *)&rw->__mlibc_rc, rc_expected | rc_waiters_bit, nullptr);
+
+ // Re-check the reader counter.
+ rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_ACQUIRE);
+ }
+
+ return 0;
+}
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) {
+ SCOPE_TRACE();
+
+ if (rw->__mlibc_flags != 0) {
+ mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
+ << frg::endlog;
+ }
+
+ // Increment the reader count while holding the __mlibc_m mutex.
+ if(int e = rwlock_m_trylock(rw, false); e)
+ return e;
+ __atomic_fetch_add(&rw->__mlibc_rc, 1, __ATOMIC_ACQUIRE);
+ rwlock_m_unlock(rw);
+
+ return 0;
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rw) {
+ SCOPE_TRACE();
+
+ if (rw->__mlibc_flags != 0) {
+ mlibc::panicLogger() << "mlibc: pthread_rwlock_t flags were non-zero"
+ << frg::endlog;
+ }
+
+ // Increment the reader count while holding the __mlibc_m mutex.
+ rwlock_m_lock(rw, false);
+ __atomic_fetch_add(&rw->__mlibc_rc, 1, __ATOMIC_ACQUIRE);
+ rwlock_m_unlock(rw);
+
+ return 0;
+}
+
+int pthread_rwlock_unlock(pthread_rwlock_t *rw) {
+ SCOPE_TRACE();
+
+ unsigned int rc_expected = __atomic_load_n(&rw->__mlibc_rc, __ATOMIC_RELAXED);
+ if(!rc_expected) {
+ // We are doing a write-unlock.
+ rwlock_m_unlock(rw);
+ return 0;
+ }else{
+ // We are doing a read-unlock.
+ while(true) {
+ unsigned int count = rc_expected & rc_count_mask;
+ __ensure(count);
+
+ // Try to decrement the count.
+ if(count == 1 && (rc_expected & rc_waiters_bit)) {
+ unsigned int desired = 0;
+ if(!__atomic_compare_exchange_n(&rw->__mlibc_rc,
+ &rc_expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
+ continue;
+
+ // Wake the futex.
+ mlibc::sys_futex_wake((int *)&rw->__mlibc_rc);
+ break;
+ }else{
+ unsigned int desired = (rc_expected & ~rc_count_mask) | (count - 1);
+ if(!__atomic_compare_exchange_n(&rw->__mlibc_rc,
+ &rc_expected, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED))
+ continue;
+ break;
+ }
+ }
+
+ return 0;
+ }
+}
+
+int pthread_getcpuclockid(pthread_t, clockid_t *) {
+ mlibc::infoLogger() << "mlibc: pthread_getcpuclockid() always returns ENOENT"
+ << frg::endlog;
+ return ENOENT;
+}
diff --git a/lib/mlibc/options/posix/generic/pwd-stubs.cpp b/lib/mlibc/options/posix/generic/pwd-stubs.cpp
new file mode 100644
index 0000000..5d8f618
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/pwd-stubs.cpp
@@ -0,0 +1,284 @@
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <bits/ensure.h>
+
+#include <mlibc/debug.hpp>
+
+namespace {
+ FILE *global_file; // Used by setpwent/getpwent/endpwent.
+
+ bool open_global_file() {
+ if(!global_file) {
+ global_file = fopen("/etc/passwd", "r");
+ if(!global_file) {
+ errno = EIO;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void close_global_file() {
+ if(global_file) {
+ fclose(global_file);
+ global_file = nullptr;
+ }
+ }
+
+ bool extract_entry(frg::string_view line, passwd *entry) {
+ frg::string_view segments[8];
+
+ // Parse the line into 7 or 8 segments.
+ size_t s = 0;
+ int n;
+ for(n = 0; n < 7; n++) {
+ size_t d = line.find_first(':', s);
+ if(d == size_t(-1))
+ break;
+ segments[n] = line.sub_string(s, d - s);
+ s = d + 1;
+ }
+ if(line.find_first(':', s) != size_t(-1))
+ return false;
+ segments[n] = line.sub_string(s, line.size() - s);
+ n++;
+
+ if(n < 7)
+ return false;
+
+ // TODO: Handle strndup() failure.
+ auto name = strndup(segments[0].data(), segments[0].size());
+ __ensure(name);
+
+ auto passwd = strndup(segments[1].data(), segments[1].size());
+ __ensure(passwd);
+
+ auto uid = segments[2].to_number<int>();
+ if(!uid)
+ return false;
+ auto gid = segments[3].to_number<int>();
+ if(!gid)
+ return false;
+
+ auto real_name = strndup(segments[4].data(), segments[4].size());
+ __ensure(real_name);
+ auto dir = strndup(segments[5].data(), segments[5].size());
+ __ensure(dir);
+ auto shell = strndup(segments[6].data(), segments[6].size());
+ __ensure(shell);
+
+ // Chop the newline off the end of shell
+ __ensure(strlen(shell) > 0);
+ shell[strlen(shell) - 1] = '\0';
+
+ entry->pw_name = name;
+ entry->pw_passwd = passwd;
+ entry->pw_uid = *uid;
+ entry->pw_gid = *gid;
+ entry->pw_dir = dir;
+ entry->pw_shell = shell;
+ entry->pw_gecos = real_name;
+ return true;
+ }
+
+ void copy_to_buffer(passwd *pwd, char *buffer, size_t size) {
+ char *pw_dir = stpcpy(buffer, pwd->pw_name) + 1;
+ free(pwd->pw_name);
+ pwd->pw_name = buffer;
+
+ char *pw_shell = stpcpy(pw_dir, pwd->pw_dir) + 1;
+ free(pwd->pw_dir);
+ pwd->pw_dir = pw_dir;
+
+ char *pw_passwd = stpcpy(pw_shell, pwd->pw_shell) + 1;
+ free(pwd->pw_shell);
+ pwd->pw_shell = pw_shell;
+
+ char *end = stpcpy(pw_passwd, pwd->pw_passwd);
+ __ensure(end <= buffer + size);
+ free(pwd->pw_passwd);
+ pwd->pw_passwd = pw_passwd;
+ }
+
+ void clear_entry(passwd *entry) {
+ free(entry->pw_name);
+ free(entry->pw_dir);
+ free(entry->pw_passwd);
+ free(entry->pw_shell);
+ entry->pw_name = nullptr;
+ entry->pw_dir = nullptr;
+ entry->pw_passwd = nullptr;
+ entry->pw_shell = nullptr;
+ }
+}
+
+struct passwd *getpwent(void) {
+ static passwd entry;
+ char line[NSS_BUFLEN_PASSWD];
+
+ if(!open_global_file()) {
+ return nullptr;
+ }
+
+ if (fgets(line, NSS_BUFLEN_PASSWD, global_file)) {
+ clear_entry(&entry);
+ if(!extract_entry(line, &entry)) {
+ errno = EINVAL; // I suppose this can be a valid errno?
+ return nullptr;
+ }
+ return &entry;
+ }
+
+ if(ferror(global_file)) {
+ errno = EIO;
+ }
+
+ return nullptr;
+}
+
+struct passwd *getpwnam(const char *name) {
+ static passwd entry;
+ auto file = fopen("/etc/passwd", "r");
+ if(!file)
+ return nullptr;
+
+ char line[NSS_BUFLEN_PASSWD];
+ while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
+ clear_entry(&entry);
+ if(!extract_entry(line, &entry))
+ continue;
+ if(!strcmp(entry.pw_name, name)) {
+ fclose(file);
+ return &entry;
+ }
+ }
+
+ int err = errno;
+ if(ferror(file)) {
+ err = EIO;
+ }
+
+ fclose(file);
+ errno = err;
+ return nullptr;
+}
+
+int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) {
+ *result = nullptr;
+ auto file = fopen("/etc/passwd", "r");
+ if(!file) {
+ return EIO;
+ }
+
+ char line[NSS_BUFLEN_PASSWD];
+ while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
+ if(!extract_entry(line, pwd))
+ continue;
+ if(!strcmp(pwd->pw_name, name)) {
+ fclose(file);
+
+ size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir)
+ + strlen(pwd->pw_shell) + strlen(pwd->pw_passwd) + 4;
+ if (size < required_size)
+ return ERANGE;
+
+ copy_to_buffer(pwd, buffer, size);
+ *result = pwd;
+ return 0;
+ }
+ }
+
+ int ret = 0;
+ if(ferror(file)) {
+ ret = EIO;
+ }
+
+ fclose(file);
+ return ret;
+}
+
+struct passwd *getpwuid(uid_t uid) {
+ static passwd entry;
+ auto file = fopen("/etc/passwd", "r");
+ if(!file)
+ return nullptr;
+
+ char line[NSS_BUFLEN_PASSWD];
+ while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
+ clear_entry(&entry);
+ if(!extract_entry(line, &entry))
+ continue;
+ if(entry.pw_uid == uid) {
+ fclose(file);
+ return &entry;
+ }
+ }
+
+ int err = ESRCH;
+ if(ferror(file)) {
+ err = EIO;
+ }
+
+ fclose(file);
+ errno = err;
+ return nullptr;
+}
+
+int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) {
+ *result = nullptr;
+ auto file = fopen("/etc/passwd", "r");
+ if(!file) {
+ return EIO;
+ }
+
+ char line[NSS_BUFLEN_PASSWD];
+ while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
+ if(!extract_entry(line, pwd))
+ continue;
+ if(pwd->pw_uid == uid) {
+ fclose(file);
+
+ size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir)
+ + strlen(pwd->pw_shell) + + strlen(pwd->pw_passwd) + 4;
+ if (size < required_size)
+ return ERANGE;
+
+ copy_to_buffer(pwd, buffer, size);
+ *result = pwd;
+ return 0;
+ }
+ }
+
+ int ret = 0;
+ if(ferror(file)) {
+ ret = EIO;
+ }
+
+ fclose(file);
+ return ret;
+}
+
+void setpwent(void) {
+ if(!open_global_file()) {
+ return;
+ }
+ rewind(global_file);
+}
+
+void endpwent(void) {
+ close_global_file();
+}
+
+int putpwent(const struct passwd *, FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+struct passwd *fgetpwent(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/resolv_conf.cpp b/lib/mlibc/options/posix/generic/resolv_conf.cpp
new file mode 100644
index 0000000..a5c3aa7
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/resolv_conf.cpp
@@ -0,0 +1,42 @@
+#include <mlibc/resolv_conf.hpp>
+#include <mlibc/allocator.hpp>
+#include <stdio.h>
+#include <ctype.h>
+
+namespace mlibc {
+
+frg::optional<struct nameserver_data> get_nameserver() {
+ auto file = fopen("/etc/resolv.conf", "r");
+ if (!file)
+ return frg::null_opt;
+
+ char line[128];
+ struct nameserver_data ret;
+ while (fgets(line, 128, file)) {
+ char *pos;
+ if (!strchr(line, '\n') && !feof(file)) {
+ // skip truncated lines
+ for (int c = getc(file); c != '\n' && c != EOF; c = getc(file));
+ continue;
+ }
+
+ // TODO(geert): resolv.conf can actually have multiple nameservers
+ // but we just pick the first one for now
+ if (!strncmp(line, "nameserver", 10) && isspace(line[10])) {
+ char *end;
+ for (pos = line + 11; isspace(*pos); pos++);
+ for (end = pos; *end && !isspace(*end); end++);
+ *end = '\0';
+ ret.name = frg::string<MemoryAllocator>(
+ pos, end - pos, getAllocator());
+ break;
+ }
+ }
+
+ fclose(file);
+ if(ret.name.empty())
+ return frg::null_opt;
+ return ret;
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/posix/generic/sched-stubs.cpp b/lib/mlibc/options/posix/generic/sched-stubs.cpp
new file mode 100644
index 0000000..9d75d50
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sched-stubs.cpp
@@ -0,0 +1,50 @@
+
+#include <bits/ensure.h>
+#include <errno.h>
+#include <limits.h>
+#include <sched.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int sched_yield(void) {
+ if(mlibc::sys_yield) {
+ mlibc::sys_yield();
+ }else{
+ // Missing sched_yield() is not an error.
+ MLIBC_MISSING_SYSDEP();
+ }
+ return 0;
+}
+
+int sched_get_priority_max(int policy) {
+ int res = 0;
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_get_max_priority, -1);
+ if(int e = sysdep(policy, &res); e) {
+ errno = e;
+ return -1;
+ }
+ return res;
+}
+
+int sched_get_priority_min(int policy) {
+ int res = 0;
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_get_min_priority, -1);
+ if(int e = sysdep(policy, &res); e) {
+ errno = e;
+ return -1;
+ }
+ return res;
+}
+
+int sched_setscheduler(pid_t, int, const struct sched_param *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int sched_getparam(pid_t, struct sched_param *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/search.cpp b/lib/mlibc/options/posix/generic/search.cpp
new file mode 100644
index 0000000..e6f8c1d
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/search.cpp
@@ -0,0 +1,151 @@
+
+#include <bits/ensure.h>
+#include <search.h>
+#include <stddef.h>
+#include <new>
+#include <mlibc/allocator.hpp>
+#include <frg/stack.hpp>
+#include <stdlib.h>
+
+struct node {
+ const void *key;
+ void *a[2];
+ int h;
+};
+
+namespace {
+ int height(struct node *node) {
+ return node ? node->h : 0;
+ }
+
+ int rotate(struct node **nodep, int side) {
+ struct node *node = *nodep;
+ struct node *x = static_cast<struct node *>(node->a[side]);
+ struct node *y = static_cast<struct node *>(x->a[!side]);
+ struct node *z = static_cast<struct node *>(x->a[side]);
+
+ int height_node = node->h;
+ int height_y = height(y);
+ if (height_y > height(z)) {
+ // Perform double rotation
+ node->a[side] = y->a[!side];
+ x->a[!side] = y->a[side];
+ y->a[!side] = node;
+ y->a[side] = x;
+ node->h = height_y;
+ x->h = height_y;
+ y->h = height_y + 1;
+ } else {
+ // Perform single rotation
+ node->a[side] = y;
+ x->a[!side] = node;
+ node->h = height_y + 1;
+ x->h = height_y + 2;
+ y = x;
+
+ }
+ *nodep = y;
+ return y->h - height_node;
+ }
+
+ int balance_tree(struct node **nodep) {
+ struct node *node = *nodep;
+ int height_a = height(static_cast<struct node *>(node->a[0]));
+ int height_b = height(static_cast<struct node *>(node->a[1]));
+ if (height_a - height_b < 2) {
+ int old = node->h;
+ node->h = height_a < height_b ? height_b + 1 : height_a + 1;
+ return node->h - old;
+ }
+
+ return rotate(nodep, height_a < height_b);
+ }
+}
+
+void *tsearch(const void *key, void **rootp, int(*compar)(const void *, const void *)) {
+ if (!rootp)
+ return NULL;
+
+ struct node *n = static_cast<struct node *>(*rootp);
+ frg::stack<struct node **, MemoryAllocator> nodes(getAllocator());
+ nodes.push(reinterpret_cast<struct node **>(rootp));
+ int c = 0;
+ for (;;) {
+ if (!n)
+ break;
+ c = compar(key, n->key);
+ if (!c)
+ return n;
+ nodes.push(reinterpret_cast<struct node **>(&n->a[c > 0]));
+ n = static_cast<struct node *>(n->a[c > 0]);
+ }
+
+ struct node *insert = static_cast<struct node*>(malloc(sizeof(struct node)));
+ if (!insert)
+ return NULL;
+ insert->key = key;
+ insert->a[0] = insert->a[1] = NULL;
+ insert->h = 1;
+
+ (*nodes.top()) = insert;
+ nodes.pop();
+ while(nodes.size() && balance_tree(nodes.top())) nodes.pop();
+ return insert;
+}
+
+// This implementation is taken from musl
+void *tfind(const void *key, void *const *rootp, int (*compar)(const void *, const void *)) {
+ if(!rootp)
+ return 0;
+
+ struct node *n = (struct node *)*rootp;
+ for(;;) {
+ if(!n)
+ break;
+ int c = compar(key, n->key);
+ if(!c)
+ break;
+ n = (struct node *)n->a[c > 0];
+ }
+ return n;
+}
+
+void *tdelete(const void *, void **, int(*compar)(const void *, const void *)) {
+ (void)compar;
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void twalk(const void *, void (*action)(const void *, VISIT, int)) {
+ (void)action;
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void tdestroy(void *, void (*free_node)(void *)) {
+ (void)free_node;
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void *lsearch(const void *key, void *base, size_t *nelp, size_t width,
+ int (*compar)(const void *, const void *)) {
+ (void)key;
+ (void)base;
+ (void)nelp;
+ (void)width;
+ (void)compar;
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void *lfind(const void *key, const void *base, size_t *nelp,
+ size_t width, int (*compar)(const void *, const void *)) {
+ (void)key;
+ (void)base;
+ (void)nelp;
+ (void)width;
+ (void)compar;
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/semaphore-stubs.cpp b/lib/mlibc/options/posix/generic/semaphore-stubs.cpp
new file mode 100644
index 0000000..d41eccb
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/semaphore-stubs.cpp
@@ -0,0 +1,114 @@
+#include <semaphore.h>
+#include <errno.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/ansi-sysdeps.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+static constexpr unsigned int semaphoreHasWaiters = static_cast<uint32_t>(1 << 31);
+static constexpr unsigned int semaphoreCountMask = static_cast<uint32_t>(1 << 31) - 1;
+
+int sem_init(sem_t *sem, int pshared, unsigned int initial_count) {
+ if (pshared) {
+ mlibc::infoLogger() << "mlibc: shared semaphores are unsuppored" << frg::endlog;
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (initial_count > SEM_VALUE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ sem->__mlibc_count = initial_count;
+
+ return 0;
+}
+
+int sem_destroy(sem_t *) {
+ return 0;
+}
+
+int sem_wait(sem_t *sem) {
+ unsigned int state = 0;
+
+ while (1) {
+ if (!(state & semaphoreCountMask)) {
+ if (__atomic_compare_exchange_n(&sem->__mlibc_count, &state, semaphoreHasWaiters,
+ false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) {
+ int e = mlibc::sys_futex_wait((int *)&sem->__mlibc_count, state, nullptr);
+ if (e == 0 || e == EAGAIN) {
+ continue;
+ } else if (e == EINTR) {
+ errno = EINTR;
+ return -1;
+ } else {
+ mlibc::panicLogger() << "sys_futex_wait() failed with error code " << e << frg::endlog;
+ }
+ }
+ } else {
+ unsigned int desired = (state - 1);
+ if (__atomic_compare_exchange_n(&sem->__mlibc_count, &state, desired, false,
+ __ATOMIC_RELAXED, __ATOMIC_RELAXED))
+ return 0;
+ }
+ }
+}
+
+int sem_timedwait(sem_t *sem, const struct timespec *) {
+ mlibc::infoLogger() << "\e[31mmlibc: sem_timedwait is implemented as sem_wait\e[0m" << frg::endlog;
+ return sem_wait(sem);
+}
+
+int sem_post(sem_t *sem) {
+ auto old_count = __atomic_load_n(&sem->__mlibc_count, __ATOMIC_RELAXED) & semaphoreCountMask;
+
+ if (old_count + 1 > SEM_VALUE_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ auto state = __atomic_exchange_n(&sem->__mlibc_count, old_count + 1, __ATOMIC_RELEASE);
+
+ if (state & semaphoreHasWaiters)
+ if (int e = mlibc::sys_futex_wake((int *)&sem->__mlibc_count); e)
+ __ensure(!"sys_futex_wake() failed");
+
+ return 0;
+}
+
+sem_t *sem_open(const char *, int, ...) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int sem_close(sem_t *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int sem_getvalue(sem_t *, int *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int sem_unlink(const char *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int sem_trywait(sem_t *sem) {
+ while (true) {
+ auto state = __atomic_load_n(&sem->__mlibc_count, __ATOMIC_ACQUIRE);
+
+ if (state & semaphoreHasWaiters) {
+ return EAGAIN;
+ }
+
+ auto desired = state - 1;
+ if (__atomic_compare_exchange_n(&sem->__mlibc_count, &state, desired, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) {
+ return 0;
+ }
+ }
+}
diff --git a/lib/mlibc/options/posix/generic/services.cpp b/lib/mlibc/options/posix/generic/services.cpp
new file mode 100644
index 0000000..8ae0656
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/services.cpp
@@ -0,0 +1,222 @@
+#include <mlibc/services.hpp>
+#include <netdb.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <mlibc/debug.hpp>
+
+namespace mlibc {
+
+static int parse_rest(service_buf &buf, char *end, int proto) {
+ if (!strncmp(end, "/udp", 4)) {
+ if (proto == IPPROTO_TCP && proto != -1)
+ return 0;
+ buf.protocol = IPPROTO_UDP;
+ buf.socktype = SOCK_DGRAM;
+ } else if (!strncmp(end, "/tcp", 4)) {
+ if (proto == IPPROTO_UDP && proto != -1)
+ return 0;
+ buf.protocol = IPPROTO_TCP;
+ buf.socktype = SOCK_STREAM;
+ } else {
+ return 0;
+ }
+
+ //TODO(geert): also parse aliases.
+
+ return 1;
+}
+
+static int lookup_serv_file_port(service_result &buf, int proto, int port) {
+ auto file = fopen(_PATH_SERVICES, "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ char line_buf[129] = {0};
+ char *line = line_buf + 1;
+ while(fgets(line, 128, file)) {
+ int name_length = 0;
+ char *pos;
+ // easy way to handle comments, just move the end of the line
+ // to the beginning of the comment
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+
+ char *end = NULL;
+ for (pos = line; *pos; pos++) {
+ for (; isalpha(*pos); pos++);
+ int rport = strtoul(pos, &end, 10);
+ if (rport != port || rport > 65535) {
+ pos = end;
+ continue;
+ }
+
+ // We have found the port, time to rewind to the start
+ // of the line.
+ for (; pos[-1]; pos--)
+ if(!isspace(pos[-1]))
+ name_length++;
+ break;
+ }
+
+ if (!pos)
+ continue;
+
+ if (!name_length)
+ continue;
+
+ auto name = frg::string<MemoryAllocator>(pos, name_length,
+ getAllocator());
+
+ struct service_buf sbuf = {};
+ sbuf.port = port;
+ sbuf.name = std::move(name);
+ if (!parse_rest(sbuf, end, proto))
+ continue;
+ buf.push_back(std::move(sbuf));
+ }
+
+ fclose(file);
+ return buf.size();
+}
+
+static int lookup_serv_file_name(service_result &buf, const char *name,
+ int proto) {
+ auto file = fopen(_PATH_SERVICES, "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ char line[128];
+ int name_length = strlen(name);
+ while(fgets(line, 128, file)) {
+ char *pos;
+ // easy way to handle comments, just move the end of the line
+ // to the beginning of the comment
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+
+ for (pos = line; (pos = strstr(pos, name)); pos++) {
+ // the name must start and end with a space
+ if (pos > line && !isspace(pos[-1]))
+ continue;
+ if (pos[name_length] && !isspace(pos[name_length]))
+ continue;
+ break;
+ }
+ if (!pos)
+ continue;
+
+ // Skip the name at the beginning of the line.
+ for(pos = line; *pos && !isspace(*pos); pos++)
+ ;
+
+ char *end = NULL;
+ int port = strtoul(pos, &end, 10);
+ if (port > 65535 || end == pos)
+ continue;
+
+ struct service_buf sbuf;
+ sbuf.port = port;
+ sbuf.name = frg::string<MemoryAllocator>(name, getAllocator());
+ if (!parse_rest(sbuf, end, proto))
+ continue;
+
+ buf.push_back(sbuf);
+
+ }
+
+ fclose(file);
+ return buf.size();
+}
+
+
+// This function returns a negative error code, since a positive
+// return code means success.
+int lookup_serv_by_name(service_result &buf, const char *name, int proto,
+ int socktype, int flags) {
+ switch(socktype) {
+ case SOCK_STREAM:
+ if (!proto)
+ proto = IPPROTO_TCP;
+ else if (proto != IPPROTO_TCP)
+ return -EAI_SERVICE;
+ break;
+ case SOCK_DGRAM:
+ if (!proto)
+ proto = IPPROTO_UDP;
+ else if (proto != IPPROTO_UDP)
+ return -EAI_SERVICE;
+ break;
+ case 0:
+ break;
+ default:
+ if (name)
+ return -EAI_SERVICE;
+ buf[0].port = 0;
+ buf[0].socktype = socktype;
+ buf[0].protocol = proto;
+ return 1;
+ }
+
+ char *end = nullptr;
+ unsigned int port = 0;
+ int count = 0;
+
+ if (name) {
+ if (!*name)
+ return -EAI_SERVICE;
+ port = strtoul(name, &end, 10);
+ }
+ // The end pointer is a null pointer so the name was a port
+ // or the name was not specified.
+ if (!end || !*end) {
+ if (proto != IPPROTO_UDP) {
+ buf[count].port = port;
+ buf[count].protocol = IPPROTO_TCP;
+ buf[count].socktype = SOCK_STREAM;
+ count++;
+ }
+ if (proto != IPPROTO_TCP) {
+ buf[count].port = port;
+ buf[count].protocol = IPPROTO_UDP;
+ buf[count].socktype = SOCK_DGRAM;
+ count++;
+ }
+ return count;
+ }
+
+ if (flags & AI_NUMERICSERV)
+ return -EAI_NONAME;
+
+ return lookup_serv_file_name(buf, name, proto);
+}
+
+int lookup_serv_by_port(service_result &buf, int proto, int port) {
+ return lookup_serv_file_port(buf, proto, port);
+}
+
+} // namespace mlibc
diff --git a/lib/mlibc/options/posix/generic/spawn-stubs.cpp b/lib/mlibc/options/posix/generic/spawn-stubs.cpp
new file mode 100644
index 0000000..cf7edfc
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/spawn-stubs.cpp
@@ -0,0 +1,376 @@
+
+#include <spawn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+/*
+ * Musl places this in a seperate header called fdop.h
+ * This header isn't present in glibc, or on my host, so I
+ * include it's contents here
+ */
+
+#define FDOP_CLOSE 1
+#define FDOP_DUP2 2
+#define FDOP_OPEN 3
+#define FDOP_CHDIR 4
+#define FDOP_FCHDIR 5
+
+struct fdop {
+ struct fdop *next, *prev;
+ int cmd, fd, srcfd, oflag;
+ mode_t mode;
+ char path[];
+};
+
+/*
+ * This posix_spawn implementation is taken from musl
+ */
+
+static unsigned long handler_set[NSIG / (8 * sizeof(long))];
+
+static void __get_handler_set(sigset_t *set) {
+ memcpy(set, handler_set, sizeof handler_set);
+}
+
+struct args {
+ int p[2];
+ sigset_t oldmask;
+ const char *path;
+ const posix_spawn_file_actions_t *fa;
+ const posix_spawnattr_t *__restrict attr;
+ char *const *argv, *const *envp;
+};
+
+static int child(void *args_vp) {
+ int i, ret;
+ struct sigaction sa = {};
+ struct args *args = (struct args *)args_vp;
+ int p = args->p[1];
+ const posix_spawn_file_actions_t *fa = args->fa;
+ const posix_spawnattr_t *__restrict attr = args->attr;
+ sigset_t hset;
+ bool use_execvpe = false;
+
+ if(attr->__fn)
+ use_execvpe = true;
+
+ close(args->p[0]);
+
+ /* All signal dispositions must be either SIG_DFL or SIG_IGN
+ * before signals are unblocked. Otherwise a signal handler
+ * from the parent might get run in the child while sharing
+ * memory, with unpredictable and dangerous results. To
+ * reduce overhead, sigaction has tracked for us which signals
+ * potentially have a signal handler. */
+ __get_handler_set(&hset);
+ for(i = 1; i < NSIG; i++) {
+ if((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember(&attr->__def, i)) {
+ sa.sa_handler = SIG_DFL;
+ } else if(sigismember(&hset, i)) {
+ if (i - 32 < 3) {
+ sa.sa_handler = SIG_IGN;
+ } else {;
+ sigaction(i, 0, &sa);
+ if(sa.sa_handler == SIG_IGN)
+ continue;
+ sa.sa_handler = SIG_DFL;
+ }
+ } else {
+ continue;
+ }
+ sigaction(i, &sa, 0);
+ }
+
+ if(attr->__flags & POSIX_SPAWN_SETSID) {
+ if((ret = setsid()) < 0)
+ goto fail;
+ }
+
+ if(attr->__flags & POSIX_SPAWN_SETPGROUP) {
+ mlibc::infoLogger() << "mlibc: posix_spawn: ignoring SETPGROUP" << frg::endlog;
+ //if((ret = setpgid(0, attr->__pgrp)))
+ // goto fail;
+ }
+
+ if(attr->__flags & POSIX_SPAWN_RESETIDS) {
+ if((ret = setgid(getgid())) || (ret = setuid(getuid())) )
+ goto fail;
+ }
+
+ if(fa && fa->__actions) {
+ struct fdop *op;
+ int fd;
+ for(op = (struct fdop *)fa->__actions; op->next; op = op->next);
+ for(; op; op = op->prev) {
+ /* It's possible that a file operation would clobber
+ * the pipe fd used for synchronizing with the
+ * parent. To avoid that, we dup the pipe onto
+ * an unoccupied fd. */
+ if(op->fd == p) {
+ ret = dup(p);
+ if(ret < 0)
+ goto fail;
+ close(p);
+ p = ret;
+ }
+ switch(op->cmd) {
+ case FDOP_CLOSE:
+ close(op->fd);
+ break;
+ case FDOP_DUP2:
+ fd = op->srcfd;
+ if(fd == p) {
+ ret = -EBADF;
+ goto fail;
+ }
+ if(fd != op->fd) {
+ if((ret = dup2(fd, op->fd)) < 0)
+ goto fail;
+ } else {
+ ret = fcntl(fd, F_GETFD);
+ ret = fcntl(fd, F_SETFD, ret & ~FD_CLOEXEC);
+ if(ret < 0)
+ goto fail;
+ }
+ break;
+ case FDOP_OPEN:
+ fd = open(op->path, op->oflag, op->mode);
+ if((ret = fd) < 0)
+ goto fail;
+ if(fd != op->fd) {
+ if((ret = dup2(fd, op->fd)) < 0)
+ goto fail;
+ close(fd);
+ }
+ break;
+ case FDOP_CHDIR:
+ ret = chdir(op->path);
+ if(ret < 0)
+ goto fail;
+ break;
+ case FDOP_FCHDIR:
+ ret = fchdir(op->fd);
+ if(ret < 0)
+ goto fail;
+ break;
+ }
+ }
+ }
+
+ /* Close-on-exec flag may have been lost if we moved the pipe
+ * to a different fd. */
+ fcntl(p, F_SETFD, FD_CLOEXEC);
+
+ pthread_sigmask(SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
+ ? &attr->__mask : &args->oldmask, 0);
+
+ if(use_execvpe)
+ execvpe(args->path, args->argv, args->envp);
+ else
+ execve(args->path, args->argv, args->envp);
+ ret = -errno;
+
+fail:
+ /* Since sizeof errno < PIPE_BUF, the write is atomic. */
+ ret = -ret;
+ if(ret)
+ while(write(p, &ret, sizeof ret) < 0);
+ _exit(127);
+}
+
+int posix_spawn(pid_t *__restrict res, const char *__restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attrs,
+ char *const argv[], char *const envp[]) {
+ pid_t pid;
+ int ec = 0, cs;
+ struct args args;
+ const posix_spawnattr_t empty_attr = {};
+ sigset_t full_sigset;
+ sigfillset(&full_sigset);
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ args.path = path;
+ args.fa = file_actions;
+ args.attr = attrs ? attrs : &empty_attr;
+ args.argv = argv;
+ args.envp = envp;
+ pthread_sigmask(SIG_BLOCK, &full_sigset, &args.oldmask);
+
+ /* The lock guards both against seeing a SIGABRT disposition change
+ * by abort and against leaking the pipe fd to fork-without-exec. */
+ //LOCK(__abort_lock);
+
+ if(pipe2(args.p, O_CLOEXEC)) {
+ //UNLOCK(__abort_lock);
+ ec = errno;
+ goto fail;
+ }
+
+ /* Mlibc change: We use fork + execve, as clone is not implemented.
+ * This yields the same result in the end. */
+ //pid = clone(child, stack + sizeof stack, CLONE_VM | CLONE_VFORK | SIGCHLD, &args);
+ pid = fork();
+ if(!pid) {
+ child(&args);
+ }
+ close(args.p[1]);
+ //UNLOCK(__abort_lock);
+
+ if(pid > 0) {
+ if(read(args.p[0], &ec, sizeof ec) != sizeof ec)
+ ec = 0;
+ else
+ waitpid(pid, 0, 0);
+ } else {
+ ec = -pid;
+ }
+
+ close(args.p[0]);
+
+ if(!ec && res)
+ *res = pid;
+
+fail:
+ pthread_sigmask(SIG_SETMASK, &args.oldmask, 0);
+ pthread_setcancelstate(cs, 0);
+
+ return ec;
+}
+
+int posix_spawnattr_init(posix_spawnattr_t *attr) {
+ *attr = (posix_spawnattr_t){};
+ return 0;
+}
+
+int posix_spawnattr_destroy(posix_spawnattr_t *) {
+ return 0;
+}
+
+int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
+ const unsigned all_flags =
+ POSIX_SPAWN_RESETIDS |
+ POSIX_SPAWN_SETPGROUP |
+ POSIX_SPAWN_SETSIGDEF |
+ POSIX_SPAWN_SETSIGMASK |
+ POSIX_SPAWN_SETSCHEDPARAM |
+ POSIX_SPAWN_SETSCHEDULER |
+ POSIX_SPAWN_USEVFORK |
+ POSIX_SPAWN_SETSID;
+ if(flags & ~all_flags)
+ return EINVAL;
+ attr->__flags = flags;
+ return 0;
+}
+
+int posix_spawnattr_setsigdefault(posix_spawnattr_t *__restrict attr,
+ const sigset_t *__restrict sigdefault) {
+ attr->__def = *sigdefault;
+ return 0;
+}
+
+int posix_spawnattr_setschedparam(posix_spawnattr_t *__restrict,
+ const struct sched_param *__restrict) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int posix_spawnattr_setschedpolicy(posix_spawnattr_t *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int posix_spawnattr_setsigmask(posix_spawnattr_t *__restrict attr,
+ const sigset_t *__restrict sigmask) {
+ attr->__mask = *sigmask;
+ return 0;
+}
+
+int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup) {
+ attr->__pgrp = pgroup;
+ return 0;
+}
+
+int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) {
+ file_actions->__actions = 0;
+ return 0;
+}
+
+int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) {
+ struct fdop *op = (struct fdop *)file_actions->__actions, *next;
+ while(op) {
+ next = op->next;
+ free(op);
+ op = next;
+ }
+ return 0;
+}
+
+int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions,
+ int fildes, int newfildes) {
+ struct fdop *op = (struct fdop *)malloc(sizeof *op);
+ if(!op)
+ return ENOMEM;
+ op->cmd = FDOP_DUP2;
+ op->srcfd = fildes;
+ op->fd = newfildes;
+ if((op->next = (struct fdop *)file_actions->__actions))
+ op->next->prev = op;
+ op->prev = 0;
+ file_actions->__actions = op;
+ return 0;
+}
+
+int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions,
+ int fildes) {
+ struct fdop *op = (struct fdop *)malloc(sizeof *op);
+ if(!op)
+ return ENOMEM;
+ op->cmd = FDOP_CLOSE;
+ op->fd = fildes;
+ if((op->next = (struct fdop *)file_actions->__actions))
+ op->next->prev = op;
+ op->prev = 0;
+ file_actions->__actions = op;
+ return 0;
+}
+
+int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict file_actions,
+ int fildes, const char *__restrict path, int oflag, mode_t mode) {
+ struct fdop *op = (struct fdop *)malloc(sizeof *op + strlen(path) + 1);
+ if(!op)
+ return ENOMEM;
+ op->cmd = FDOP_OPEN;
+ op->fd = fildes;
+ op->oflag = oflag;
+ op->mode = mode;
+ strcpy(op->path, path);
+ if((op->next = (struct fdop *)file_actions->__actions))
+ op->next->prev = op;
+ op->prev = 0;
+ file_actions->__actions = op;
+ return 0;
+}
+
+int posix_spawnp(pid_t *__restrict pid, const char *__restrict file,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attrp,
+ char *const argv[], char *const envp[]) {
+ posix_spawnattr_t spawnp_attr = {};
+ if(attrp)
+ spawnp_attr = *attrp;
+ spawnp_attr.__fn = (void *)execvpe;
+ return posix_spawn(pid, file, file_actions, &spawnp_attr, argv, envp);
+}
+
diff --git a/lib/mlibc/options/posix/generic/strings-stubs.cpp b/lib/mlibc/options/posix/generic/strings-stubs.cpp
new file mode 100644
index 0000000..524c424
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/strings-stubs.cpp
@@ -0,0 +1,107 @@
+
+#include <strings.h>
+#include <string.h>
+
+#include <ctype.h>
+#include <bits/ensure.h>
+#include <mlibc/strings.hpp>
+
+char *index (const char *s, int c) {
+ return strchr(s, c);
+}
+
+char *rindex(const char *s, int c) {
+ return strrchr(s, c);
+}
+
+namespace {
+
+ template<typename T>
+ int ffs_generic(T i) {
+ //Non-portably assume a byte has 8 bits; fine in all plausible cases.
+ for(size_t b = 0; b < sizeof(T) * 8;)
+ if(i & (static_cast<T>(0x1) << b++))
+ return b;
+
+ return 0;
+ }
+
+}
+
+// On RISC-V, __builtin_ffs just calls into ffs, so we can't use it here.
+#if defined(__has_builtin) && !defined(__riscv)
+# if __has_builtin(__builtin_ffs)
+# define __mlibc_ffs __builtin_ffs
+# endif
+# if __has_builtin(__builtin_ffsl)
+# define __mlibc_ffsl __builtin_ffsl
+# endif
+# if __has_builtin(__builtin_ffsll)
+# define __mlibc_ffsll __builtin_ffsll
+# endif
+#endif
+
+int ffs(int i) {
+#ifdef __mlibc_ffs
+ return __mlibc_ffs(i);
+#else
+ return ffs_generic<int>(i);
+#endif
+}
+
+/*
+ Both ffsl() and ffsll() are glibc extensions
+ defined in string.h. They are however implemented
+ here because of similarity in logic and
+ shared code.
+*/
+
+int ffsl(long i) {
+#ifdef __mlibc_ffsl
+ return __mlibc_ffsl(i);
+#else
+ return ffs_generic<long>(i);
+#endif
+}
+
+int ffsll(long long i) {
+#ifdef __mlibc_ffsll
+ return __mlibc_ffsll(i);
+#else
+ return ffs_generic<long long>(i);
+#endif
+}
+
+int strcasecmp(const char *a, const char *b) {
+ size_t i = 0;
+ while(true) {
+ unsigned char a_byte = tolower(a[i]);
+ unsigned char b_byte = tolower(b[i]);
+ if(!a_byte && !b_byte)
+ return 0;
+ // If only one char is null, one of the following cases applies.
+ if(a_byte < b_byte)
+ return -1;
+ if(a_byte > b_byte)
+ return 1;
+ i++;
+ }
+}
+
+int strncasecmp(const char *a, const char *b, size_t size) {
+ return mlibc::strncasecmp(a, b, size);
+}
+
+// Marked as obsolete in posix 2008 but used by at least tracker
+int bcmp(const void *s1, const void *s2, size_t n) {
+ return memcmp(s1, s2, n);
+}
+
+void bcopy(const void *s1, void *s2, size_t n) {
+ memmove(s2, s1, n);
+}
+
+void bzero(void *s, size_t n) {
+ memset(s, 0, n);
+}
+
diff --git a/lib/mlibc/options/posix/generic/sys-file-stubs.cpp b/lib/mlibc/options/posix/generic/sys-file-stubs.cpp
new file mode 100644
index 0000000..e1cc9ab
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-file-stubs.cpp
@@ -0,0 +1,16 @@
+
+#include <sys/file.h>
+#include <mlibc/posix-sysdeps.hpp>
+#include <errno.h>
+
+#include <bits/ensure.h>
+
+int flock(int fd, int opt) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_flock, -1);
+ if(int e = mlibc::sys_flock(fd, opt); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/lib/mlibc/options/posix/generic/sys-ipc.cpp b/lib/mlibc/options/posix/generic/sys-ipc.cpp
new file mode 100644
index 0000000..b9e0d3d
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-ipc.cpp
@@ -0,0 +1,8 @@
+#include <sys/ipc.h>
+
+#include <bits/ensure.h>
+
+key_t ftok(const char *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/sys-mman-stubs.cpp b/lib/mlibc/options/posix/generic/sys-mman-stubs.cpp
new file mode 100644
index 0000000..9383976
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-mman-stubs.cpp
@@ -0,0 +1,177 @@
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <bits/ensure.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int mprotect(void *pointer, size_t size, int prot) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_vm_protect, -1);
+ if(int e = mlibc::sys_vm_protect(pointer, size, prot); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int mlock(const void *addr, size_t len) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mlock, -1);
+ if(int e = mlibc::sys_mlock(addr, len); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int mlockall(int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mlockall, -1);
+ if(int e = mlibc::sys_mlockall(flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int munlock(const void *addr, size_t len) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_munlock, -1);
+ if(int e = mlibc::sys_munlock(addr, len); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int munlockall(void) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_munlockall, -1);
+ if(int e = mlibc::sys_munlockall(); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+
+int posix_madvise(void *, size_t, int) {
+ mlibc::infoLogger() << "\e[31m" "mlibc: posix_madvise() fails unconditionally" "\e[39m"
+ << frg::endlog;
+ return ENOSYS;
+}
+
+int msync(void *addr, size_t length, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_msync, -1);
+ if(int e = mlibc::sys_msync(addr, length, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+void *mremap(void *pointer, size_t size, size_t new_size, int flags, ...) {
+ __ensure(flags == MREMAP_MAYMOVE);
+
+ void *window;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_vm_remap, (void *)-1);
+ if(int e = mlibc::sys_vm_remap(pointer, size, new_size, &window); e) {
+ errno = e;
+ return (void *)-1;
+ }
+ return window;
+}
+
+int remap_file_pages(void *, size_t, int, size_t, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void *mmap(void *hint, size_t size, int prot, int flags, int fd, off_t offset) {
+ void *window;
+ if(int e = mlibc::sys_vm_map(hint, size, prot, flags, fd, offset, &window); e) {
+ errno = e;
+ return (void *)-1;
+ }
+ return window;
+}
+
+int munmap(void *pointer, size_t size) {
+ if(int e = mlibc::sys_vm_unmap(pointer, size); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+// The implementation of shm_open and shm_unlink is taken from musl.
+namespace {
+ char *shm_mapname(const char *name, char *buf) {
+ char *p;
+ while(*name == '/')
+ name++;
+ if(*(p = strchrnul(name, '/')) || p == name ||
+ (p - name <= 2 && name[0] == '.' && p[-1] == '.')) {
+ errno = EINVAL;
+ return 0;
+ }
+ if(p - name > NAME_MAX) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+ memcpy(buf, "/dev/shm/", 9);
+ memcpy(buf + 9, name, p - name + 1);
+ return buf;
+ }
+}
+
+int shm_open(const char *name, int flags, mode_t mode) {
+ int cs;
+ char buf[NAME_MAX + 10];
+ if(!(name = shm_mapname(name, buf)))
+ return -1;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ int fd = open(name, flags | O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK, mode);
+ pthread_setcancelstate(cs, 0);
+ return fd;
+}
+
+int shm_unlink(const char *name) {
+ char buf[NAME_MAX + 10];
+ if(!(name = shm_mapname(name, buf)))
+ return -1;
+ return unlink(name);
+}
+
+#if __MLIBC_LINUX_OPTION
+int memfd_create(const char *name, unsigned int flags) {
+ int ret = -1;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_memfd_create, -1);
+ if(int e = mlibc::sys_memfd_create(name, flags, &ret)) {
+ errno = e;
+ return -1;
+ }
+
+ return ret;
+}
+
+int madvise(void *addr, size_t length, int advice) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_madvise, -1);
+ if(int e = mlibc::sys_madvise(addr, length, advice)) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int mincore(void *addr, size_t length, unsigned char *vec) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_munlockall, -1);
+ if(int e = mlibc::sys_mincore(addr, length, vec); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+#endif /* __MLIBC_LINUX_OPTION */
diff --git a/lib/mlibc/options/posix/generic/sys-msg.cpp b/lib/mlibc/options/posix/generic/sys-msg.cpp
new file mode 100644
index 0000000..95f067e
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-msg.cpp
@@ -0,0 +1,23 @@
+
+#include <bits/ensure.h>
+#include <sys/msg.h>
+
+int msgget(key_t, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int msgctl(int, int, struct msqid_ds *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+ssize_t msgrcv(int, void *, size_t, long, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int msgsnd(int, const void *, size_t, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/sys-resource-stubs.cpp b/lib/mlibc/options/posix/generic/sys-resource-stubs.cpp
new file mode 100644
index 0000000..597de4d
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-resource-stubs.cpp
@@ -0,0 +1,57 @@
+
+#include <errno.h>
+#include <sys/resource.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int getpriority(int which, id_t who) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getpriority, -1);
+ int value = 0;
+ if(int e = mlibc::sys_getpriority(which, who, &value); e) {
+ errno = e;
+ }
+ return value;
+}
+
+int setpriority(int which, id_t who, int prio) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setpriority, -1);
+ if(int e = mlibc::sys_setpriority(which, who, prio); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int getrusage(int scope, struct rusage *usage) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getrusage, -1);
+ if(int e = mlibc::sys_getrusage(scope, usage); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int getrlimit(int resource, struct rlimit *limit) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getrlimit, -1);
+ if(int e = mlibc::sys_getrlimit(resource, limit); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setrlimit(int resource, const struct rlimit *limit) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setrlimit, -1);
+ if(int e = mlibc::sys_setrlimit(resource, limit); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int prlimit(pid_t, int, const struct rlimit *, struct rlimit *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/sys-select-stubs.cpp b/lib/mlibc/options/posix/generic/sys-select-stubs.cpp
new file mode 100644
index 0000000..e8ff927
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-select-stubs.cpp
@@ -0,0 +1,58 @@
+
+#include <string.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <bits/ensure.h>
+#include <mlibc-config.h>
+
+#include <mlibc/posix-sysdeps.hpp>
+
+void __FD_CLR(int fd, fd_set *set) {
+ __ensure(fd < FD_SETSIZE);
+ set->__mlibc_elems[fd / 8] &= ~(1 << (fd % 8));
+}
+int __FD_ISSET(int fd, fd_set *set) {
+ __ensure(fd < FD_SETSIZE);
+ return set->__mlibc_elems[fd / 8] & (1 << (fd % 8));
+}
+void __FD_SET(int fd, fd_set *set) {
+ __ensure(fd < FD_SETSIZE);
+ set->__mlibc_elems[fd / 8] |= 1 << (fd % 8);
+}
+void __FD_ZERO(fd_set *set) {
+ memset(set->__mlibc_elems, 0, sizeof(fd_set));
+}
+
+int select(int num_fds, fd_set *__restrict read_set, fd_set *__restrict write_set,
+ fd_set *__restrict except_set, struct timeval *__restrict timeout) {
+ int num_events = 0;
+ struct timespec timeouts = {};
+ struct timespec *timeout_ptr = NULL;
+ if (timeout) {
+ timeouts.tv_sec = timeout->tv_sec;
+ timeouts.tv_nsec = timeout->tv_usec * 1000;
+ timeout_ptr = &timeouts;
+ }
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pselect, -1);
+ if(int e = mlibc::sys_pselect(num_fds, read_set, write_set, except_set,
+ timeout_ptr, NULL, &num_events); e) {
+ errno = e;
+ return -1;
+ }
+ return num_events;
+}
+
+int pselect(int num_fds, fd_set *read_set, fd_set *write_set, fd_set *except_set,
+ const struct timespec *timeout, const sigset_t *sigmask) {
+ int num_events = 0;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pselect, -1);
+ if(int e = mlibc::sys_pselect(num_fds, read_set, write_set, except_set,
+ timeout, sigmask, &num_events); e) {
+ errno = e;
+ return -1;
+ }
+ return num_events;
+}
diff --git a/lib/mlibc/options/posix/generic/sys-sem.cpp b/lib/mlibc/options/posix/generic/sys-sem.cpp
new file mode 100644
index 0000000..ac3df69
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-sem.cpp
@@ -0,0 +1,51 @@
+
+#include <bits/ensure.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/sem.h>
+
+#include <mlibc/posix-sysdeps.hpp>
+
+int semget(key_t key, int n, int fl) {
+ if(n > USHRT_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int id = 0;
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_semget, -1);
+ if(int e = sysdep(key, n, fl, &id); e) {
+ errno = e;
+ return -1;
+ }
+ return id;
+}
+
+int semop(int, struct sembuf *, size_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+};
+
+int semctl(int id, int num, int cmd, ...) {
+ union semun semun;
+ int ret = 0;
+
+ va_list ap;
+ va_start(ap, cmd);
+ semun = va_arg(ap, union semun);
+ va_end(ap);
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_semctl, -1);
+ if(int e = sysdep(id, num, cmd, semun.buf, &ret); e) {
+ errno = e;
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/lib/mlibc/options/posix/generic/sys-shm.cpp b/lib/mlibc/options/posix/generic/sys-shm.cpp
new file mode 100644
index 0000000..3af7e90
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-shm.cpp
@@ -0,0 +1,24 @@
+#include <sys/shm.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+void *shmat(int, const void *, int) {
+ __ensure(!"Function is not implemented");
+ __builtin_unreachable();
+}
+
+int shmctl(int, int, struct shmid_ds *) {
+ __ensure(!"Function is not implemented");
+ __builtin_unreachable();
+}
+
+int shmdt(const void *) {
+ __ensure(!"Function is not implemented");
+ __builtin_unreachable();
+}
+
+int shmget(key_t, size_t, int) {
+ mlibc::infoLogger() << "mlibc: shmget() is a no-op!" << frg::endlog;
+ return -1;
+}
diff --git a/lib/mlibc/options/posix/generic/sys-socket-stubs.cpp b/lib/mlibc/options/posix/generic/sys-socket-stubs.cpp
new file mode 100644
index 0000000..037a994
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-socket-stubs.cpp
@@ -0,0 +1,225 @@
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/socket.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int accept(int fd, struct sockaddr *__restrict addr_ptr, socklen_t *__restrict addr_length) {
+ int newfd;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_accept, -1);
+ if(int e = mlibc::sys_accept(fd, &newfd, addr_ptr, addr_length, 0); e) {
+ errno = e;
+ return -1;
+ }
+ return newfd;
+}
+
+int accept4(int fd, struct sockaddr *__restrict addr_ptr, socklen_t *__restrict addr_length, int flags) {
+ int newfd;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_accept, -1);
+ if(int e = mlibc::sys_accept(fd, &newfd, addr_ptr, addr_length, flags); e) {
+ errno = e;
+ return -1;
+ }
+
+ return newfd;
+}
+
+int bind(int fd, const struct sockaddr *addr_ptr, socklen_t addr_len) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_bind, -1);
+ if(int e = mlibc::sys_bind(fd, addr_ptr, addr_len); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int connect(int fd, const struct sockaddr *addr_ptr, socklen_t addr_len) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_connect, -1);
+ if(int e = mlibc::sys_connect(fd, addr_ptr, addr_len); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int getpeername(int fd, struct sockaddr *addr_ptr, socklen_t *__restrict addr_length) {
+ socklen_t actual_length;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_peername, -1);
+ if(int e = mlibc::sys_peername(fd, addr_ptr, *addr_length, &actual_length); e) {
+ errno = e;
+ return -1;
+ }
+ *addr_length = actual_length;
+ return 0;
+}
+
+int getsockname(int fd, struct sockaddr *__restrict addr_ptr, socklen_t *__restrict addr_length) {
+ socklen_t actual_length;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sockname, -1);
+ if(int e = mlibc::sys_sockname(fd, addr_ptr, *addr_length, &actual_length); e) {
+ errno = e;
+ return -1;
+ }
+ *addr_length = actual_length;
+ return 0;
+}
+
+int getsockopt(int fd, int layer, int number,
+ void *__restrict buffer, socklen_t *__restrict size) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getsockopt, -1);
+ return mlibc::sys_getsockopt(fd, layer, number, buffer, size);
+}
+
+int listen(int fd, int backlog) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_listen, -1);
+ if(int e = mlibc::sys_listen(fd, backlog); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+ssize_t recv(int sockfd, void *__restrict buf, size_t len, int flags) {
+ return recvfrom(sockfd, buf, len, flags, NULL, NULL);
+}
+
+ssize_t recvfrom(int sockfd, void *__restrict buf, size_t len, int flags,
+ struct sockaddr *__restrict src_addr, socklen_t *__restrict addrlen) {
+ if(mlibc::sys_recvfrom) {
+ ssize_t length;
+ if(int e = mlibc::sys_recvfrom(sockfd, buf, len, flags, src_addr, addrlen, &length); e) {
+ errno = e;
+ return -1;
+ }
+ return length;
+ }
+
+ struct iovec iov = {};
+ iov.iov_base = buf;
+ iov.iov_len = len;
+
+ struct msghdr hdr = {};
+ hdr.msg_name = src_addr;
+ if (addrlen) {
+ hdr.msg_namelen = *addrlen;
+ }
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+
+ int ret = recvmsg(sockfd, &hdr, flags);
+ if (ret < 0)
+ return ret;
+
+ if(addrlen)
+ *addrlen = hdr.msg_namelen;
+ return ret;
+}
+
+ssize_t recvmsg(int fd, struct msghdr *hdr, int flags) {
+ ssize_t length;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_msg_recv, -1);
+ if(int e = mlibc::sys_msg_recv(fd, hdr, flags, &length); e) {
+ errno = e;
+ return -1;
+ }
+ return length;
+}
+
+int recvmmsg(int, struct mmsghdr *, unsigned int, int, struct timespec *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+ssize_t send(int fd, const void *buffer, size_t size, int flags) {
+ return sendto(fd, buffer, size, flags, nullptr, 0);
+}
+
+ssize_t sendto(int fd, const void *buffer, size_t size, int flags,
+ const struct sockaddr *sock_addr, socklen_t addr_length) {
+ if(mlibc::sys_sendto) {
+ ssize_t length;
+ if(int e = mlibc::sys_sendto(fd, buffer, size, flags, sock_addr, addr_length, &length); e) {
+ errno = e;
+ return -1;
+ }
+ return length;
+ }
+
+ struct iovec iov = {};
+ iov.iov_base = const_cast<void *>(buffer);
+ iov.iov_len = size;
+
+ struct msghdr hdr = {};
+ hdr.msg_name = const_cast<struct sockaddr *>(sock_addr);
+ hdr.msg_namelen = addr_length;
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+
+ return sendmsg(fd, &hdr, flags);
+}
+
+ssize_t sendmsg(int fd, const struct msghdr *hdr, int flags) {
+ if(hdr->msg_iovlen > IOV_MAX)
+ return EMSGSIZE;
+
+ ssize_t length;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_msg_send, -1);
+ if(int e = mlibc::sys_msg_send(fd, hdr, flags, &length); e) {
+ errno = e;
+ return -1;
+ }
+ return length;
+}
+
+int sendmmsg(int, struct mmsghdr *, unsigned int, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int setsockopt(int fd, int layer, int number,
+ const void *buffer, socklen_t size) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setsockopt, -1);
+ return mlibc::sys_setsockopt(fd, layer, number, buffer, size);
+}
+
+int shutdown(int sockfd, int how) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_shutdown, -1);
+ if(int e = sysdep(sockfd, how); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int sockatmark(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int socket(int family, int type, int protocol) {
+ int fd;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_socket, -1);
+ if(int e = mlibc::sys_socket(family, type, protocol, &fd); e) {
+ errno = e;
+ return -1;
+ }
+ return fd;
+}
+
+int socketpair(int domain, int type, int protocol, int sv[2]) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_socketpair, -1);
+ if(int e = mlibc::sys_socketpair(domain, type, protocol, sv); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+// connectpair() is provided by the platform
+
diff --git a/lib/mlibc/options/posix/generic/sys-stat-stubs.cpp b/lib/mlibc/options/posix/generic/sys-stat-stubs.cpp
new file mode 100644
index 0000000..2eca445
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-stat-stubs.cpp
@@ -0,0 +1,155 @@
+
+#include <errno.h>
+#include <bits/ensure.h>
+#include <sys/stat.h>
+
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int chmod(const char *pathname, mode_t mode) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_chmod, -1);
+ if(int e = mlibc::sys_chmod(pathname, mode); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fchmod(int fd, mode_t mode) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchmod, -1);
+ if(int e = mlibc::sys_fchmod(fd, mode); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchmodat, -1);
+ if(int e = mlibc::sys_fchmodat(dirfd, pathname, mode, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fstatat(int dirfd, const char *path, struct stat *result, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1);
+ if(int e = mlibc::sys_stat(mlibc::fsfd_target::fd_path, dirfd, path, flags, result); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int futimens(int fd, const struct timespec times[2]) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1);
+
+ if (int e = mlibc::sys_utimensat(fd, nullptr, times, 0); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int mkdir(const char *path, mode_t mode) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mkdir, -1);
+ if(int e = mlibc::sys_mkdir(path, mode); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int mkdirat(int dirfd, const char *path, mode_t mode) {
+ mlibc::infoLogger() << "\e[31mmlibc: mkdirat() ignores its mode\e[39m" << frg::endlog;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mkdirat, -1);
+ if(int e = mlibc::sys_mkdirat(dirfd, path, mode); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int mkfifo(const char *path, mode_t mode) {
+ return mkfifoat(AT_FDCWD, path, mode);
+}
+
+int mkfifoat(int dirfd, const char *path, mode_t mode) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mkfifoat, -1);
+ if (int e = mlibc::sys_mkfifoat(dirfd, path, mode); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int mknod(const char *path, mode_t mode, dev_t dev) {
+ return mknodat(AT_FDCWD, path, mode, dev);
+}
+
+int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_mknodat, -1);
+ if (int e = mlibc::sys_mknodat(dirfd, path, mode, dev); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+mode_t umask(mode_t mode) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_umask, -1);
+ mode_t old;
+ if (int e = mlibc::sys_umask(mode, &old); e) {
+ errno = e;
+ return -1;
+ }
+ return old;
+}
+
+int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags) {
+ if(pathname == nullptr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1);
+ if (int e = mlibc::sys_utimensat(dirfd, pathname, times, flags); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
+int stat(const char *path, struct stat *result) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1);
+ if(int e = mlibc::sys_stat(mlibc::fsfd_target::path, -1, path, 0, result); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int lstat(const char *path, struct stat *result) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1);
+ if(int e = mlibc::sys_stat(mlibc::fsfd_target::path,
+ -1, path, AT_SYMLINK_NOFOLLOW, result); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fstat(int fd, struct stat *result) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_stat, -1);
+ if(int e = mlibc::sys_stat(mlibc::fsfd_target::fd, fd, "", 0, result); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp b/lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp
new file mode 100644
index 0000000..c02cc39
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-statvfs-stubs.cpp
@@ -0,0 +1,24 @@
+#include <errno.h>
+#include <sys/statvfs.h>
+
+#include <bits/ensure.h>
+#include <mlibc/posix-sysdeps.hpp>
+
+int statvfs(const char *path, struct statvfs *out) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_statvfs, -1);
+ if(int e = mlibc::sys_statvfs(path, out); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fstatvfs(int fd, struct statvfs *out) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fstatvfs, -1);
+ if(int e = mlibc::sys_fstatvfs(fd, out); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/lib/mlibc/options/posix/generic/sys-time-stubs.cpp b/lib/mlibc/options/posix/generic/sys-time-stubs.cpp
new file mode 100644
index 0000000..5cc0fe5
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-time-stubs.cpp
@@ -0,0 +1,107 @@
+
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+int gettimeofday(struct timeval *__restrict result, void *__restrict unused) {
+ (void)unused; // Linux just ignores gettimeofday().
+
+ if(result) {
+ long nanos;
+ if(int e = mlibc::sys_clock_get(CLOCK_REALTIME, &result->tv_sec, &nanos); e) {
+ errno = e;
+ return -1;
+ }
+ result->tv_usec = nanos / 1000;
+ }
+ return 0;
+}
+
+int settimeofday(const struct timeval *, const struct timezone *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *res) {
+ res->tv_sec = a->tv_sec + b->tv_sec;
+ res->tv_usec = a->tv_usec + b->tv_usec;
+ while(res->tv_usec > 999999) {
+ res->tv_usec -= 1000000;
+ res->tv_sec += 1;
+ }
+}
+
+void timersub(const struct timeval *a, const struct timeval *b, struct timeval *res) {
+ res->tv_sec = a->tv_sec - b->tv_sec;
+ res->tv_usec = a->tv_usec - b->tv_usec;
+ while(res->tv_usec < 0) {
+ res->tv_usec += 1000000;
+ res->tv_sec -= 1;
+ }
+}
+
+void timerclear(struct timeval *tvp) {
+ tvp->tv_sec = 0;
+ tvp->tv_usec = 0;
+}
+
+int timerisset(struct timeval *tvp) {
+ if(tvp->tv_sec != 0 || tvp->tv_usec != 0) {
+ return 1;
+ }
+ return 0;
+}
+
+int getitimer(int which, struct itimerval *curr_value) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getitimer, -1);
+ if(int e = mlibc::sys_getitimer(which, curr_value); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setitimer, -1);
+ if(int e = mlibc::sys_setitimer(which, new_value, old_value); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int timer_create(clockid_t clk, struct sigevent *__restrict evp, timer_t *__restrict res) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_timer_create, -1);
+ if(int e = mlibc::sys_timer_create(clk, evp, res); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int timer_settime(timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_timer_settime, -1);
+ if(int e = mlibc::sys_timer_settime(t, flags, val, old); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int timer_gettime(timer_t, struct itimerspec *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int timer_delete(timer_t t) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_timer_delete, -1);
+ if(int e = mlibc::sys_timer_delete(t); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/mlibc/options/posix/generic/sys-times.cpp b/lib/mlibc/options/posix/generic/sys-times.cpp
new file mode 100644
index 0000000..61b6e25
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-times.cpp
@@ -0,0 +1,19 @@
+
+#include <errno.h>
+#include <sys/times.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <internal-config.h>
+#include <mlibc/posix-sysdeps.hpp>
+
+clock_t times(struct tms *tms) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_times, -1);
+ clock_t ret;
+ if(int e = mlibc::sys_times(tms, &ret); e) {
+ errno = e;
+ return -1;
+ }
+ return ret;
+}
+
diff --git a/lib/mlibc/options/posix/generic/sys-uio.cpp b/lib/mlibc/options/posix/generic/sys-uio.cpp
new file mode 100644
index 0000000..0f14bc0
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-uio.cpp
@@ -0,0 +1,67 @@
+
+#include <sys/uio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <frg/vector.hpp>
+#include <mlibc/allocator.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <bits/ensure.h>
+
+ssize_t readv(int fd, const struct iovec *iovs, int iovc) {
+ ssize_t read_bytes = 0;
+
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_readv, -1);
+
+ if (int e = sysdep(fd, iovs, iovc, &read_bytes); e) {
+ errno = e;
+ return -1;
+ }
+
+ return read_bytes;
+}
+
+ssize_t writev(int fd, const struct iovec *iovs, int iovc) {
+ __ensure(iovc);
+
+ ssize_t written = 0;
+ size_t bytes = 0;
+ for(int i = 0; i < iovc; i++) {
+ if(SSIZE_MAX - bytes < iovs[i].iov_len) {
+ errno = EINVAL;
+ return -1;
+ }
+ bytes += iovs[i].iov_len;
+ }
+ frg::vector<char, MemoryAllocator> buffer{getAllocator()};
+ buffer.resize(bytes);
+
+ size_t to_copy = bytes;
+ char *bp = buffer.data();
+ for(int i = 0; i < iovc; i++) {
+ size_t copy = frg::min(iovs[i].iov_len, to_copy);
+
+ bp = (char *)mempcpy((void *)bp, (void *)iovs[i].iov_base, copy);
+
+ to_copy -= copy;
+ if(to_copy == 0)
+ break;
+ }
+
+ written = write(fd, buffer.data(), bytes);
+ return written;
+}
+
+ssize_t preadv(int, const struct iovec *, int, off_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+ssize_t pwritev(int, const struct iovec *, int, off_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/sys-utsname.cpp b/lib/mlibc/options/posix/generic/sys-utsname.cpp
new file mode 100644
index 0000000..3176574
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-utsname.cpp
@@ -0,0 +1,24 @@
+
+#include <string.h>
+#include <sys/utsname.h>
+#include <errno.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <internal-config.h>
+#include <mlibc/posix-sysdeps.hpp>
+
+int uname(struct utsname *p) {
+ if (p == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_uname, -1);
+ if(int e = mlibc::sys_uname(p); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/lib/mlibc/options/posix/generic/sys-wait-stubs.cpp b/lib/mlibc/options/posix/generic/sys-wait-stubs.cpp
new file mode 100644
index 0000000..6e7cc78
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/sys-wait-stubs.cpp
@@ -0,0 +1,52 @@
+
+#include <errno.h>
+#include <sys/wait.h>
+#include <bits/ensure.h>
+
+#include <mlibc/ansi-sysdeps.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+
+int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitid, -1);
+ if(int e = sysdep(idtype, id, info, options); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+pid_t waitpid(pid_t pid, int *status, int flags) {
+ pid_t ret;
+ int tmp_status = 0;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1);
+ if(int e = mlibc::sys_waitpid(pid, &tmp_status, flags, NULL, &ret); e) {
+ errno = e;
+ return -1;
+ }
+ if(status) {
+ *status = tmp_status;
+ }
+ return ret;
+}
+
+pid_t wait(int *status) {
+ return waitpid(-1, status, 0);
+}
+
+pid_t wait3(int *status, int options, struct rusage *rusage) {
+ (void) rusage;
+ mlibc::infoLogger() << "\e[31mmlibc: wait3() is not implemented correctly\e[39m"
+ << frg::endlog;
+ return waitpid(-1, status, options);
+}
+
+pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru) {
+ pid_t ret;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1);
+ if(int e = mlibc::sys_waitpid(pid, status, options, ru, &ret); e) {
+ errno = e;
+ return -1;
+ }
+ return ret;
+}
diff --git a/lib/mlibc/options/posix/generic/syslog-stubs.cpp b/lib/mlibc/options/posix/generic/syslog-stubs.cpp
new file mode 100644
index 0000000..6a80ff9
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/syslog-stubs.cpp
@@ -0,0 +1,152 @@
+
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <bits/ensure.h>
+
+#include <frg/mutex.hpp>
+#include <mlibc/lock.hpp>
+#include <mlibc/debug.hpp>
+
+// This syslog implementation is largely taken from musl
+
+static char log_ident[32];
+static int log_options;
+static int log_facility = LOG_USER;
+static int log_fd = -1;
+static int log_opt;
+static int log_mask = 0xff;
+
+static int use_mlibc_logger = 0;
+static FutexLock __syslog_lock;
+
+static const struct sockaddr_un log_addr {AF_UNIX, "/dev/log"};
+
+void closelog(void) {
+ frg::unique_lock<FutexLock> holder { __syslog_lock };
+ close(log_fd);
+ log_fd = -1;
+}
+
+static void __openlog() {
+ log_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if(log_fd >= 0) {
+ int ret = connect(log_fd, (const sockaddr *)&log_addr, sizeof log_addr);
+ if(ret) {
+ mlibc::infoLogger() << "\e[31mmlibc: syslog: connect returned an error, falling back to infoLogger\e[39m" << frg::endlog;
+ use_mlibc_logger = 1;
+ }
+ }
+}
+
+void openlog(const char *ident, int options, int facility) {
+ frg::unique_lock<FutexLock> holder { __syslog_lock };
+ if(ident) {
+ size_t n = strnlen(ident, sizeof log_ident - 1);
+ memcpy(log_ident, ident, n);
+ log_ident[n] = 0;
+ } else {
+ log_ident[0] = 0;
+ }
+ log_options = options;
+ log_facility = facility;
+
+ if((options & LOG_NDELAY) && log_fd < 0)
+ __openlog();
+}
+
+int setlogmask(int mask) {
+ int old_mask = log_mask;
+
+ log_mask = mask;
+
+ return old_mask;
+}
+
+static void _vsyslog(int priority, const char *message, va_list ap) {
+ auto is_lost_conn = [] (int e) {
+ return e == ECONNREFUSED || e == ECONNRESET || e == ENOTCONN || e == EPIPE;
+ };
+
+ if(!(priority & log_mask)) {
+ return;
+ }
+
+ char timebuf[16];
+ time_t now;
+ struct tm tm;
+ char buf[1024];
+ int errno_save = errno;
+ int pid;
+ int l, l2;
+ int hlen;
+ int fd;
+
+ if(log_fd < 0)
+ __openlog();
+
+ if(use_mlibc_logger) {
+ vsnprintf(buf, sizeof buf, message, ap);
+ mlibc::infoLogger() << "mlibc: syslog: " << buf << frg::endlog;
+ return;
+ }
+
+ if(!(priority & LOG_FACMASK))
+ priority |= log_facility;
+
+ now = time(NULL);
+ gmtime_r(&now, &tm);
+ strftime(timebuf, sizeof timebuf, "%b %e %T", &tm);
+
+ pid = (log_opt & LOG_PID) ? getpid() : 0;
+ l = snprintf(buf, sizeof buf, "<%d>%s %n%s%s%.0d%s: ",
+ priority, timebuf, &hlen, log_ident, "[" + !pid, pid, "]" + !pid);
+ errno = errno_save;
+ l2 = vsnprintf(buf + l, sizeof buf - l, message, ap);
+ if(l2 >= 0) {
+ if(l2 >= (long int)(sizeof buf - l))
+ l = sizeof buf - 1;
+ else
+ l += l2;
+ if(buf[l - 1] != '\n')
+ buf[l++] = '\n';
+ if(send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
+ || connect(log_fd, (const sockaddr *)&log_addr, sizeof log_addr) < 0
+ || send(log_fd, buf, l, 0) < 0)
+ && (log_opt & LOG_CONS)) {
+ fd = open("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if(fd >= 0) {
+ dprintf(fd, "%.*s", l - hlen, buf + hlen);
+ close(fd);
+ }
+ }
+ if(log_opt & LOG_PERROR)
+ dprintf(STDERR_FILENO, "%.*s", l - hlen, buf + hlen);
+ }
+}
+
+void syslog(int priority, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ vsyslog(priority, format, ap);
+ va_end(ap);
+}
+
+void vsyslog(int priority, const char *message, va_list ap) {
+ int cs;
+ if(!(log_mask & LOG_MASK(priority & 7)) || (priority & ~0x3ff)) {
+ mlibc::infoLogger() << "\e[31mmlibc: syslog: log_mask or priority out of range, not printing anything\e[39m" << frg::endlog;
+ return;
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ frg::unique_lock<FutexLock> lock(__syslog_lock);
+ _vsyslog(priority, message, ap);
+ pthread_setcancelstate(cs, 0);
+}
diff --git a/lib/mlibc/options/posix/generic/termios-stubs.cpp b/lib/mlibc/options/posix/generic/termios-stubs.cpp
new file mode 100644
index 0000000..631456a
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/termios-stubs.cpp
@@ -0,0 +1,103 @@
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/posix-sysdeps.hpp>
+
+speed_t cfgetispeed(const struct termios *tios) {
+ return tios->c_cflag & CBAUD;
+}
+
+speed_t cfgetospeed(const struct termios *tios) {
+ return tios->c_cflag & CBAUD;
+}
+
+int cfsetispeed(struct termios *termios, speed_t speed) {
+ return speed ? cfsetospeed(termios, speed) : 0;
+}
+
+int cfsetospeed(struct termios *termios, speed_t speed) {
+ if(speed & ~CBAUD) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ termios->c_cflag &= ~CBAUD;
+ termios->c_cflag |= speed;
+
+ return 0;
+}
+
+void cfmakeraw(struct termios *t) {
+ t->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+ t->c_oflag &= ~OPOST;
+ t->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ t->c_cflag &= ~(CSIZE | PARENB);
+ t->c_cflag |= CS8;
+ t->c_cc[VMIN] = 1;
+ t->c_cc[VTIME] = 0;
+}
+
+int tcdrain(int fd) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcdrain, -1);
+ if(int e = mlibc::sys_tcdrain(fd); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int tcflow(int fd, int action) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcflow, -1);
+ if(int e = mlibc::sys_tcflow(fd, action); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int tcflush(int fd, int queue_selector) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcflush, -1);
+ if(int e = mlibc::sys_tcflush(fd, queue_selector); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int tcgetattr(int fd, struct termios *attr) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcgetattr, -1);
+ if(int e = mlibc::sys_tcgetattr(fd, attr); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+pid_t tcgetsid(int fd) {
+ int sid;
+ if(ioctl(fd, TIOCGSID, &sid) < 0) {
+ return -1;
+ }
+ return sid;
+}
+
+int tcsendbreak(int, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int tcsetattr(int fd, int opts, const struct termios *attr) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tcsetattr, -1);
+ if(int e = mlibc::sys_tcsetattr(fd, opts, attr); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/lib/mlibc/options/posix/generic/time.cpp b/lib/mlibc/options/posix/generic/time.cpp
new file mode 100644
index 0000000..14193af
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/time.cpp
@@ -0,0 +1,505 @@
+#include <ctype.h>
+#include <langinfo.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <bits/ensure.h>
+#include <mlibc/strings.hpp>
+
+namespace {
+
+int month_to_day(int month) {
+ switch(month){
+ case 0: return 0;
+ case 1: return 31;
+ case 2: return 59;
+ case 3: return 90;
+ case 4: return 120;
+ case 5: return 151;
+ case 6: return 181;
+ case 7: return 212;
+ case 8: return 243;
+ case 9: return 273;
+ case 10: return 304;
+ case 11: return 334;
+ }
+ return -1;
+}
+
+int is_leapyear(int year) {
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+}
+
+int month_and_year_to_day_in_year(int month, int year){
+ int day = month_to_day(month);
+ if(is_leapyear(year) && month < 2)
+ return day + 1;
+
+ return day;
+}
+
+int target_determination(int month) {
+ switch(month){
+ case 0: return 3;
+ case 1: return 14;
+ case 2: return 14;
+ case 3: return 4;
+ case 4: return 9;
+ case 5: return 6;
+ case 6: return 11;
+ case 7: return 8;
+ case 8: return 5;
+ case 9: return 10;
+ case 10: return 7;
+ case 11: return 12;
+ }
+
+ return -1;
+}
+
+int doom_determination(int full_year) {
+ int century = full_year / 100;
+ int anchor = 2 + 5 * (century % 4) % 7;
+
+ int year = full_year % 100;
+
+ if(year % 2)
+ year += 11;
+
+ year /= 2;
+
+ if(year % 2)
+ year += 11;
+
+ return 7 - (year % 7) + anchor;
+}
+
+//Determine day of week through the doomsday algorithm.
+int day_determination(int day, int month, int year) {
+ int doom = doom_determination(year);
+ bool leap = is_leapyear(year);
+
+ int target = target_determination(month);
+ if(leap && month < 2)
+ target++;
+
+ int doom_dif = (day - target) % 7;
+ return (doom + doom_dif) % 7;
+}
+
+struct strptime_internal_state {
+ bool has_century;
+ bool has_year;
+ bool has_month;
+ bool has_day_of_month;
+ bool has_day_of_year;
+ bool has_day_of_week;
+
+ bool full_year_given;
+
+ int century;
+
+ size_t format_index;
+ size_t input_index;
+};
+
+char *strptime_internal(const char *__restrict input, const char *__restrict format,
+ struct tm *__restrict tm, struct strptime_internal_state *__restrict state) {
+ auto matchLanginfoItem = [&] (int start, size_t num, int &dest, bool &flag) -> bool {
+ for(size_t i = start; i < (start + num); i++) {
+ const char *mon = nl_langinfo(i);
+ size_t len = strlen(mon);
+ if(mlibc::strncasecmp(&input[state->input_index], mon, len))
+ continue;
+ state->input_index += len;
+ dest = i - start;
+ flag = true;
+ return true;
+ }
+ return false;
+ };
+
+ auto matchNumericRange = [&] (int start, int end, int &dest, bool *flag) -> bool {
+ int product = 0, n = 0;
+ sscanf(&input[state->input_index], "%d%n", &product, &n);
+ if(n == 0 || 2 < n)
+ return false;
+ if(product < start || product > end)
+ return false;
+ state->input_index += n;
+ dest = product;
+ if(flag) *flag = true;
+ return true;
+ };
+
+ while(isspace(input[state->input_index]))
+ state->input_index++;
+
+ if(input[state->input_index] == '\0')
+ return NULL;
+
+ while(format[state->format_index] != '\0'){
+ if(format[state->format_index] != '%'){
+ if(isspace(format[state->format_index])){
+ while(isspace(input[state->input_index++]));
+ state->input_index--;
+ }
+ else {
+ if(format[state->format_index] != input[state->input_index++])
+ return NULL;
+ }
+ state->format_index++;
+ continue;
+ }
+ state->format_index++;
+ switch(format[state->format_index]){
+ case '%':
+ if(input[state->input_index++] != '%')
+ return NULL;
+ break;
+ case 'a':
+ case 'A': {
+ if (!matchLanginfoItem(DAY_1, 7, tm->tm_wday, state->has_day_of_week) && \
+ !matchLanginfoItem(ABDAY_1, 7, tm->tm_wday, state->has_day_of_week))
+ return NULL;
+ break;
+ }
+ case 'b':
+ case 'B':
+ case 'h': {
+ if (!matchLanginfoItem(MON_1, 12, tm->tm_mon, state->has_month) && \
+ !matchLanginfoItem(ABMON_1, 12, tm->tm_mon, state->has_month))
+ return NULL;
+ break;
+ }
+ case 'c':
+ __ensure(!"strptime() %c directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'C': {
+ int product = 0, n = 0;
+ sscanf(&input[state->input_index], "%d%n", &product, &n);
+ if(n == 0 || 2 < n)
+ return NULL;
+ state->input_index += n;
+ state->century = product;
+ state->has_century = true;
+ break;
+ }
+ case 'd': //`%d` and `%e` are equivalent
+ case 'e': {
+ if(!matchNumericRange(1, 31, tm->tm_mday, &state->has_day_of_month))
+ return NULL;
+ break;
+ }
+ case 'D': { //equivalent to `%m/%d/%y`
+ size_t pre_fi = state->format_index;
+ state->format_index = 0;
+
+ char *result = strptime_internal(input, "%m/%d/%y", tm, state);
+ if(result == NULL)
+ return NULL;
+
+ state->format_index = pre_fi;
+ break;
+ }
+ case 'H': {
+ if(!matchNumericRange(0, 23, tm->tm_hour, nullptr))
+ return NULL;
+ break;
+ }
+ case 'I': {
+ if(!matchNumericRange(1, 12, tm->tm_hour, nullptr))
+ return NULL;
+ break;
+ }
+ case 'j': {
+ if(!matchNumericRange(1, 366, tm->tm_yday, &state->has_day_of_year))
+ return NULL;
+ tm->tm_yday--;
+ break;
+ }
+ case 'm': {
+ if(!matchNumericRange(1, 12, tm->tm_mon, &state->has_month))
+ return NULL;
+ tm->tm_mon--;
+ break;
+ }
+ case 'M': {
+ if(!matchNumericRange(0, 59, tm->tm_min, nullptr))
+ return NULL;
+ break;
+ }
+ case 'n':
+ case 't': {
+ size_t n = 0;
+ while(isspace(input[state->input_index++]))
+ n++;
+ if(n == 0)
+ return NULL;
+ state->input_index--;
+ break;
+ }
+ case 'p': {
+ const char *meridian_str = nl_langinfo(AM_STR);
+ size_t len = strlen(meridian_str);
+ if (!mlibc::strncasecmp(&input[state->input_index], meridian_str, len)) {
+ tm->tm_hour %= 12;
+ state->input_index += len;
+ break;
+ }
+ meridian_str = nl_langinfo(PM_STR);
+ len = strlen(meridian_str);
+ if (!mlibc::strncasecmp(&input[state->input_index], meridian_str, len)) {
+ tm->tm_hour %= 12;
+ tm->tm_hour += 12;
+ state->input_index += len;
+ break;
+ }
+ break;
+ }
+ case 'r': { //equivalent to `%I:%M:%S %p`
+ size_t pre_fi = state->format_index;
+ state->format_index = 0;
+
+ char *result = strptime_internal(input, "%I:%M:%S %p", tm, state);
+ if(result == NULL)
+ return NULL;
+
+ state->format_index = pre_fi;
+ break;
+ }
+ case 'R': { //equivalent to `%H:%M`
+ size_t pre_fi = state->format_index;
+ state->format_index = 0;
+
+ char *result = strptime_internal(input, "%H:%M", tm, state);
+ if(result == NULL)
+ return NULL;
+
+ state->format_index = pre_fi;
+ break;
+ }
+ case 'S': {
+ if(!matchNumericRange(0, 60, tm->tm_sec, nullptr))
+ return NULL;
+ break;
+ }
+ case 'T': { //equivalent to `%H:%M:%S`
+ size_t pre_fi = state->format_index;
+ state->format_index = 0;
+
+ char *result = strptime_internal(input, "%H:%M:%S", tm, state);
+ if(result == NULL)
+ return NULL;
+
+ state->format_index = pre_fi;
+ break;
+ }
+ case 'U':
+ __ensure(!"strptime() %U directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'w': {
+ int product = 0, n = 0;
+ sscanf(&input[state->input_index], "%d%n", &product, &n);
+ if(n == 0 || 1 < n)
+ return NULL;
+ state->input_index += n;
+ tm->tm_wday = product;
+ state->has_day_of_week = true;
+ break;
+ }
+ case 'W':
+ __ensure(!"strptime() %W directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'x':
+ __ensure(!"strptime() %x directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'X':
+ __ensure(!"strptime() %X directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'y': {
+ int product = 0, n = 0;
+ sscanf(&input[state->input_index], "%d%n", &product, &n);
+ if(n == 0 || 2 < n)
+ return NULL;
+ if(product < 69)
+ product += 100;
+ state->input_index += n;
+ tm->tm_year = product;
+ state->has_year = true;
+ break;
+ }
+ case 'Y': {
+ int product = 0, n = 0;
+ sscanf(&input[state->input_index], "%d%n", &product, &n);
+ if(n == 0 || 4 < n)
+ return NULL;
+ state->input_index += n;
+ tm->tm_year = product - 1900;
+ state->has_year = true;
+ state->has_century = true;
+ state->full_year_given = true;
+ state->century = product / 100;
+ break;
+ }
+ case 'F': { //GNU extensions
+ //equivalent to `%Y-%m-%d`
+ size_t pre_fi = state->format_index;
+ state->format_index = 0;
+
+ char *result = strptime_internal(input, "%Y-%m-%d", tm, state);
+ if(result == NULL)
+ return NULL;
+
+ state->format_index = pre_fi;
+ break;
+ }
+ case 'g':
+ __ensure(!"strptime() %g directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'G':
+ __ensure(!"strptime() %G directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'u': {
+ if(!matchNumericRange(1, 7, tm->tm_wday, nullptr))
+ return NULL;
+ tm->tm_wday--;
+ break;
+ }
+ case 'V':
+ __ensure(!"strptime() %V directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'z':
+ __ensure(!"strptime() %z directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'Z':
+ __ensure(!"strptime() %Z directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 's': //end of GNU extensions
+ __ensure(!"strptime() %s directive unimplemented.");
+ __builtin_unreachable();
+ break;
+ case 'E': { //locale-dependent date & time representation
+ __ensure(!"strptime() %E* directives unimplemented.");
+ __builtin_unreachable();
+ /*
+ state->format_index++;
+ switch(format[state->format_index]){
+ case 'c':
+ break;
+ case 'C':
+ break;
+ case 'x':
+ break;
+ case 'X':
+ break;
+ case 'y':
+ break;
+ case 'Y':
+ break;
+ default:
+ return NULL;
+ }
+ */
+ }
+ case 'O': { //locale-dependent numeric symbols
+ __ensure(!"strptime() %O* directives unimplemented.");
+ __builtin_unreachable();
+ /*
+ state->format_index++;
+ switch(format[state->format_index]){
+ case 'd':
+ case 'e':
+ break;
+ case 'H':
+ break;
+ case 'I':
+ break;
+ case 'm':
+ break;
+ case 'M':
+ break;
+ case 'S':
+ break;
+ case 'U':
+ break;
+ case 'w':
+ break;
+ case 'W':
+ break;
+ case 'y':
+ break;
+ default:
+ return NULL;
+ }
+ */
+ }
+ default:
+ return NULL;
+ }
+ state->format_index++;
+ }
+
+ return (char*)input + state->input_index;
+}
+
+} //anonymous namespace
+
+char *strptime(const char *__restrict s, const char *__restrict format, struct tm *__restrict tm){
+ struct strptime_internal_state state = {};
+
+ char *result = strptime_internal(s, format, tm, &state);
+
+ if(result == NULL)
+ return NULL;
+
+ if(state.has_century && !state.full_year_given){
+ int full_year = state.century * 100;
+
+ if(state.has_year){
+ //Compensate for default century-adjustment of `%j` operand
+ if(tm->tm_year >= 100)
+ full_year += tm->tm_year - 100;
+ else
+ full_year += tm->tm_year;
+ }
+
+ tm->tm_year = full_year - 1900;
+
+ state.has_year = true;
+ }
+
+ if(state.has_month && !state.has_day_of_year){
+ int day = 0;
+ if(state.has_year)
+ day = month_and_year_to_day_in_year(tm->tm_mon, tm->tm_year);
+ else
+ day = month_to_day(tm->tm_mon);
+
+ tm->tm_yday = day + tm->tm_mday - 1;
+ state.has_day_of_year = true;
+ }
+
+ if(state.has_year && !state.has_day_of_week){
+ if(!state.has_month && !state.has_day_of_month){
+ tm->tm_wday = day_determination(0, 0, tm->tm_year + 1900);
+ }
+ else if(state.has_month && state.has_day_of_month){
+ tm->tm_wday = day_determination(tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
+ }
+ state.has_day_of_week = true;
+ }
+
+ return result;
+}
diff --git a/lib/mlibc/options/posix/generic/ucontext-stubs.cpp b/lib/mlibc/options/posix/generic/ucontext-stubs.cpp
new file mode 100644
index 0000000..9413a78
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/ucontext-stubs.cpp
@@ -0,0 +1,19 @@
+#include <ucontext.h>
+#include <bits/ensure.h>
+
+int getcontext(ucontext_t *) {
+ __ensure(!"Not implemented!");
+ __builtin_unreachable();
+}
+int setcontext(const ucontext_t *) {
+ __ensure(!"Not implemented!");
+ __builtin_unreachable();
+}
+void makecontext(ucontext_t *, void (*)(), int, ...) {
+ __ensure(!"Not implemented!");
+ __builtin_unreachable();
+}
+int swapcontext(ucontext_t *, const ucontext_t *) {
+ __ensure(!"Not implemented!");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/posix/generic/unistd-stubs.cpp b/lib/mlibc/options/posix/generic/unistd-stubs.cpp
new file mode 100644
index 0000000..39cf76a
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/unistd-stubs.cpp
@@ -0,0 +1,1227 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <limits.h>
+#include <termios.h>
+#include <pwd.h>
+#include <sys/ioctl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/allocator.hpp>
+#include <mlibc/arch-defs.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+#include <mlibc/bsd-sysdeps.hpp>
+#include <mlibc/thread.hpp>
+
+namespace {
+
+constexpr bool logExecvpeTries = false;
+
+}
+
+unsigned int alarm(unsigned int seconds) {
+ struct itimerval it = {}, old = {};
+ it.it_value.tv_sec = seconds;
+ setitimer(ITIMER_REAL, &it, &old);
+ return old.it_value.tv_sec + !! old.it_value.tv_usec;
+}
+
+int chdir(const char *path) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_chdir, -1);
+ if(int e = mlibc::sys_chdir(path); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fchdir(int fd) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchdir, -1);
+ if(int e = mlibc::sys_fchdir(fd); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int chown(const char *path, uid_t uid, gid_t gid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1);
+ if(int e = mlibc::sys_fchownat(AT_FDCWD, path, uid, gid, 0); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+ssize_t confstr(int name, char *buf, size_t len) {
+ const char *str = "";
+ if (name == _CS_PATH) {
+ str = "/bin:/usr/bin";
+ } else if(name == _CS_GNU_LIBPTHREAD_VERSION) {
+ // We are not glibc, so we can return 0 here.
+ return 0;
+ } else if(name == _CS_GNU_LIBC_VERSION) {
+ // We are not glibc, so we can return 0 here.
+ return 0;
+ } else {
+ mlibc::infoLogger() << "\e[31mmlibc: confstr() request " << name << " is unimplemented\e[39m"
+ << frg::endlog;
+ __ensure(!"Not implemented");
+ }
+
+ return snprintf(buf, len, "%s", str) + 1;
+}
+
+void _exit(int status) {
+ mlibc::sys_exit(status);
+}
+
+int execl(const char *path, const char *arg0, ...) {
+ // TODO: It's a stupid idea to limit the number of arguments here.
+ char *argv[16];
+ argv[0] = const_cast<char *>(arg0);
+
+ va_list args;
+ int n = 1;
+ va_start(args, arg0);
+ while(true) {
+ __ensure(n < 15);
+ auto argn = va_arg(args, const char *);
+ argv[n++] = const_cast<char *>(argn);
+ if(!argn)
+ break;
+ }
+ va_end(args);
+ argv[n] = nullptr;
+
+ return execve(path, argv, environ);
+}
+
+// This function is taken from musl.
+int execle(const char *path, const char *arg0, ...) {
+ int argc;
+ va_list ap;
+ va_start(ap, arg0);
+ for(argc = 1; va_arg(ap, const char *); argc++);
+ va_end(ap);
+
+ int i;
+ char *argv[argc + 1];
+ char **envp;
+ va_start(ap, arg0);
+ argv[0] = (char *)argv;
+ for(i = 1; i <= argc; i++)
+ argv[i] = va_arg(ap, char *);
+ envp = va_arg(ap, char **);
+ va_end(ap);
+ return execve(path, argv, envp);
+}
+
+// This function is taken from musl
+int execlp(const char *file, const char *argv0, ...) {
+ int argc;
+ va_list ap;
+ va_start(ap, argv0);
+ for(argc = 1; va_arg(ap, const char *); argc++);
+ va_end(ap);
+ {
+ int i;
+ char *argv[argc + 1];
+ va_start(ap, argv0);
+ argv[0] = (char *)argv0;
+ for(i = 1; i < argc; i++)
+ argv[i] = va_arg(ap, char *);
+ argv[i] = NULL;
+ va_end(ap);
+ return execvp(file, argv);
+ }
+}
+
+int execv(const char *path, char *const argv[]) {
+ return execve(path, argv, environ);
+}
+
+int execvp(const char *file, char *const argv[]) {
+ return execvpe(file, argv, environ);
+}
+
+int execvpe(const char *file, char *const argv[], char *const envp[]) {
+ char *null_list[] = {
+ nullptr
+ };
+
+ if(!argv)
+ argv = null_list;
+ if(!envp)
+ envp = null_list;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_execve, -1);
+
+ if(strchr(file, '/')) {
+ int e = mlibc::sys_execve(file, argv, envp);
+ __ensure(e && "sys_execve() is supposed to never return with success");
+ errno = e;
+ return -1;
+ }
+
+ frg::string_view dirs;
+ if(const char *pv = getenv("PATH"); pv) {
+ dirs = pv;
+ }else{
+ dirs = "/bin:/usr/bin";
+ }
+
+ size_t p = 0;
+ int res = ENOENT;
+ while(p < dirs.size()) {
+ size_t s; // Offset of next colon or end of string.
+ if(size_t cs = dirs.find_first(':', p); cs != size_t(-1)) {
+ s = cs;
+ }else{
+ s = dirs.size();
+ }
+
+ frg::string<MemoryAllocator> path{getAllocator()};
+ path += dirs.sub_string(p, s - p);
+ path += "/";
+ path += file;
+
+ if(logExecvpeTries)
+ mlibc::infoLogger() << "mlibc: execvpe() tries '" << path.data() << "'" << frg::endlog;
+
+ int e = mlibc::sys_execve(path.data(), argv, envp);
+ __ensure(e && "sys_execve() is supposed to never return with success");
+ switch(e) {
+ case ENOENT:
+ case ENOTDIR:
+ break;
+ case EACCES:
+ res = EACCES;
+ break;
+ default:
+ errno = e;
+ return -1;
+ }
+
+ p = s + 1;
+ }
+
+ errno = res;
+ return -1;
+}
+
+int faccessat(int dirfd, const char *pathname, int mode, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_faccessat, -1);
+ if(int e = mlibc::sys_faccessat(dirfd, pathname, mode, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fchown(int fd, uid_t uid, gid_t gid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1);
+ if(int e = mlibc::sys_fchownat(fd, "", uid, gid, AT_EMPTY_PATH); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fchownat(int fd, const char *path, uid_t uid, gid_t gid, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1);
+ if(int e = mlibc::sys_fchownat(fd, path, uid, gid, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fdatasync(int fd) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fdatasync, -1);
+ if(int e = mlibc::sys_fdatasync(fd); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int fexecve(int, char *const [], char *const []) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+long fpathconf(int, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int fsync(int fd) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fsync, -1);
+ if(auto e = mlibc::sys_fsync(fd); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int ftruncate(int fd, off_t size) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ftruncate, -1);
+ if(int e = mlibc::sys_ftruncate(fd, size); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+char *getcwd(char *buffer, size_t size) {
+ if (buffer) {
+ if (size == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ } else if (!buffer) {
+ if (size == 0)
+ size = PATH_MAX;
+
+ buffer = (char *)malloc(size);
+ }
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getcwd, nullptr);
+ if(int e = mlibc::sys_getcwd(buffer, size); e) {
+ errno = e;
+ return NULL;
+ }
+
+ return buffer;
+}
+
+int getgroups(int size, gid_t list[]) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getgroups, -1);
+ int ret;
+ if(int e = mlibc::sys_getgroups(size, list, &ret); e) {
+ errno = e;
+ return -1;
+ }
+ return ret;
+}
+
+long gethostid(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int gethostname(char *buffer, size_t bufsize) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_gethostname, -1);
+ if(auto e = mlibc::sys_gethostname(buffer, bufsize); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int sethostname(const char *buffer, size_t bufsize) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_sethostname, -1);
+ if(auto e = mlibc::sys_sethostname(buffer, bufsize); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+// Code taken from musl
+char *getlogin(void) {
+ return getenv("LOGNAME");
+}
+
+int getlogin_r(char *, size_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// optarg and optind are provided to us by the GLIBC part of the mlibc.
+
+static char *scan = NULL; /* Private scan pointer. */
+
+int getopt(int argc, char *const argv[], const char *optstring) {
+ char c;
+ char *place;
+
+ optarg = NULL;
+
+ if (!scan || *scan == '\0') {
+ if (optind == 0)
+ optind++;
+
+ if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
+ return EOF;
+ if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
+ optind++;
+ return EOF;
+ }
+
+ scan = argv[optind]+1;
+ optind++;
+ }
+
+ c = *scan++;
+ place = strchr(optstring, c);
+
+ if (!place || c == ':') {
+ fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
+ return '?';
+ }
+
+ place++;
+ if (*place == ':') {
+ if (*scan != '\0') {
+ optarg = scan;
+ scan = NULL;
+ } else if( optind < argc ) {
+ optarg = argv[optind];
+ optind++;
+ } else {
+ fprintf(stderr, "%s: option requires argument -%c\n", argv[0], c);
+ return ':';
+ }
+ }
+
+ return c;
+}
+
+pid_t getpgid(pid_t pid) {
+ pid_t pgid;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getpgid, -1);
+ if(int e = mlibc::sys_getpgid(pid, &pgid); e) {
+ errno = e;
+ return -1;
+ }
+ return pgid;
+}
+
+pid_t getpgrp(void) {
+ return getpgid(0);
+}
+
+pid_t getsid(pid_t pid) {
+ if(!mlibc::sys_getsid) {
+ MLIBC_MISSING_SYSDEP();
+ return -1;
+ }
+ pid_t sid;
+ if(int e = mlibc::sys_getsid(pid, &sid); e) {
+ errno = e;
+ return -1;
+ }
+ return sid;
+}
+
+int lchown(const char *path, uid_t uid, gid_t gid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fchownat, -1);
+ if(int e = mlibc::sys_fchownat(AT_FDCWD, path, uid, gid, AT_SYMLINK_NOFOLLOW); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int link(const char *old_path, const char *new_path) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_link, -1);
+ if(int e = mlibc::sys_link(old_path, new_path); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_linkat, -1);
+ if(int e = mlibc::sys_linkat(olddirfd, old_path, newdirfd, new_path, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+// Code take from musl
+int lockf(int fd, int op, off_t size) {
+ struct flock l = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_CUR,
+ .l_start = 0,
+ .l_len = size,
+ .l_pid = 0,
+ };
+
+ switch(op) {
+ case F_TEST:
+ l.l_type = F_RDLCK;
+ if(fcntl(fd, F_GETLK, &l) < 0)
+ return -1;
+ if(l.l_type == F_UNLCK || l.l_pid == getpid())
+ return 0;
+ errno = EACCES;
+ return -1;
+ case F_ULOCK:
+ l.l_type = F_UNLCK;
+ [[fallthrough]];
+ case F_TLOCK:
+ return fcntl(fd, F_SETLK, &l);
+ case F_LOCK:
+ return fcntl(fd, F_SETLKW, &l);
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+int nice(int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+long pathconf(const char *, int name) {
+ switch (name) {
+ case _PC_NAME_MAX:
+ return NAME_MAX;
+ default:
+ mlibc::infoLogger() << "missing pathconf() entry " << name << frg::endlog;
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+int pause(void) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pause, -1);
+ if(int e = mlibc::sys_pause(); e) {
+ errno = e;
+ return -1;
+ }
+ __ensure(!"There is no successful completion return value for pause");
+ __builtin_unreachable();
+}
+
+int pipe(int *fds) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pipe, -1);
+ if(int e = mlibc::sys_pipe(fds, 0); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int pipe2(int *fds, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pipe, -1);
+ if(int e = mlibc::sys_pipe(fds, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+ssize_t pread(int fd, void *buf, size_t n, off_t off) {
+ ssize_t num_read;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pread, -1);
+ if(int e = mlibc::sys_pread(fd, buf, n, off, &num_read); e) {
+ errno = e;
+ return -1;
+ }
+ return num_read;
+}
+
+ssize_t pwrite(int fd, const void *buf, size_t n, off_t off) {
+ ssize_t num_written;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_pwrite, -1);
+ if(int e = mlibc::sys_pwrite(fd, buf, n, off, &num_written); e) {
+ errno = e;
+ return -1;
+ }
+ return num_written;
+}
+
+ssize_t readlink(const char *__restrict path, char *__restrict buffer, size_t max_size) {
+ ssize_t length;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_readlink, -1);
+ if(int e = mlibc::sys_readlink(path, buffer, max_size, &length); e) {
+ errno = e;
+ return -1;
+ }
+ return length;
+}
+
+ssize_t readlinkat(int, const char *__restrict, char *__restrict, size_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int rmdir(const char *path) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_rmdir, -1);
+ if(int e = mlibc::sys_rmdir(path); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setegid(gid_t egid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setegid, 0);
+ if(int e = mlibc::sys_setegid(egid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int seteuid(uid_t euid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_seteuid, 0);
+ if(int e = mlibc::sys_seteuid(euid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setgid(gid_t gid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setgid, 0);
+ if(int e = mlibc::sys_setgid(gid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setpgid(pid_t pid, pid_t pgid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setpgid, -1);
+ if(int e = mlibc::sys_setpgid(pid, pgid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+pid_t setpgrp(void) {
+ return setpgid(0, 0);
+}
+
+int setregid(gid_t rgid, gid_t egid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setregid, -1);
+ if(int e = mlibc::sys_setregid(rgid, egid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setreuid(uid_t ruid, uid_t euid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setreuid, -1);
+ if(int e = mlibc::sys_setreuid(ruid, euid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+pid_t setsid(void) {
+ if(!mlibc::sys_setsid) {
+ MLIBC_MISSING_SYSDEP();
+ return -1;
+ }
+ pid_t sid;
+ if(int e = mlibc::sys_setsid(&sid); e) {
+ errno = e;
+ return -1;
+ }
+ return sid;
+}
+
+int setuid(uid_t uid) {
+ if(!mlibc::sys_setuid) {
+ MLIBC_MISSING_SYSDEP();
+ mlibc::infoLogger() << "mlibc: missing sysdep sys_setuid(). Returning 0" << frg::endlog;
+ return 0;
+ }
+ if(int e = mlibc::sys_setuid(uid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+void swab(const void *__restrict, void *__restrict, ssize_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int symlink(const char *target_path, const char *link_path) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_symlink, -1);
+ if(int e = mlibc::sys_symlink(target_path, link_path); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int symlinkat(const char *target_path, int dirfd, const char *link_path) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_symlinkat, -1);
+ if(int e = mlibc::sys_symlinkat(target_path, dirfd, link_path); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+void sync(void) {
+ if(!mlibc::sys_sync) {
+ MLIBC_MISSING_SYSDEP();
+ } else {
+ mlibc::sys_sync();
+ }
+}
+
+long sysconf(int number) {
+ if(mlibc::sys_sysconf) {
+ long ret = 0;
+
+ int e = mlibc::sys_sysconf(number, &ret);
+
+ if(e && e != EINVAL) {
+ errno = e;
+ return -1;
+ }
+
+ if(e != EINVAL) {
+ return ret;
+ }
+ }
+
+ /* default return values, if not overriden by sysdep */
+ switch(number) {
+ case _SC_ARG_MAX:
+ // On linux, it is defined to 2097152 in most cases, so define it to be 2097152
+ return 2097152;
+ case _SC_PAGE_SIZE:
+ return mlibc::page_size;
+ case _SC_OPEN_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_OPEN_MAX) returns fallback value 256\e[39m" << frg::endlog;
+ return 256;
+ case _SC_PHYS_PAGES:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_PHYS_PAGES) returns fallback value 1024\e[39m" << frg::endlog;
+ return 1024;
+ case _SC_NPROCESSORS_ONLN:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_NPROCESSORS_ONLN) returns fallback value 1\e[39m" << frg::endlog;
+ return 1;
+ case _SC_GETPW_R_SIZE_MAX:
+ return NSS_BUFLEN_PASSWD;
+ case _SC_GETGR_R_SIZE_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_GETGR_R_SIZE_MAX) returns fallback value 1024\e[39m" << frg::endlog;
+ return 1024;
+ case _SC_CHILD_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_CHILD_MAX) returns fallback value 25\e[39m" << frg::endlog;
+ // On linux, it is defined to 25 in most cases, so define it to be 25
+ return 25;
+ case _SC_JOB_CONTROL:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_JOB_CONTROL) returns fallback value 1\e[39m" << frg::endlog;
+ // If 1, job control is supported
+ return 1;
+ case _SC_CLK_TCK:
+ // TODO: This should be obsolete?
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_CLK_TCK) is obsolete and returns arbitrary value 1000000\e[39m" << frg::endlog;
+ return 1000000;
+ case _SC_NGROUPS_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_NGROUPS_MAX) returns fallback value 65536\e[39m" << frg::endlog;
+ // On linux, it is defined to 65536 in most cases, so define it to be 65536
+ return 65536;
+ case _SC_RE_DUP_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_RE_DUP_MAX) returns fallback value RE_DUP_MAX\e[39m" << frg::endlog;
+ return RE_DUP_MAX;
+ case _SC_LINE_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_LINE_MAX) returns fallback value 2048\e[39m" << frg::endlog;
+ // Linux defines it as 2048.
+ return 2048;
+ case _SC_XOPEN_CRYPT:
+#if __MLIBC_CRYPT_OPTION
+ return _XOPEN_CRYPT;
+#else
+ return -1;
+#endif /* __MLIBC_CRYPT_OPTION */
+ case _SC_NPROCESSORS_CONF:
+ // TODO: actually return a proper value for _SC_NPROCESSORS_CONF
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_NPROCESSORS_CONF) unconditionally returns fallback value 1\e[39m" << frg::endlog;
+ return 1;
+ case _SC_HOST_NAME_MAX:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf(_SC_HOST_NAME_MAX) unconditionally returns fallback value 256\e[39m" << frg::endlog;
+ return 256;
+ default:
+ mlibc::infoLogger() << "\e[31mmlibc: sysconf() call is not implemented, number: " << number << "\e[39m" << frg::endlog;
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+pid_t tcgetpgrp(int fd) {
+ int pgrp;
+ if(ioctl(fd, TIOCGPGRP, &pgrp) < 0) {
+ return -1;
+ }
+ return pgrp;
+}
+
+int tcsetpgrp(int fd, pid_t pgrp) {
+ return ioctl(fd, TIOCSPGRP, &pgrp);
+}
+
+int truncate(const char *, off_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+char *ttyname(int fd) {
+ const size_t size = 128;
+ static thread_local char buf[size];
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ttyname, nullptr);
+ if(int e = mlibc::sys_ttyname(fd, buf, size); e) {
+ errno = e;
+ return nullptr;
+ }
+ return buf;
+}
+
+int ttyname_r(int fd, char *buf, size_t size) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ttyname, -1);
+ if(int e = mlibc::sys_ttyname(fd, buf, size); e) {
+ return e;
+ }
+ return 0;
+}
+
+int unlink(const char *path) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlinkat, -1);
+ if(int e = mlibc::sys_unlinkat(AT_FDCWD, path, 0); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int unlinkat(int fd, const char *path, int flags) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_unlinkat, -1);
+ if(int e = mlibc::sys_unlinkat(fd, path, flags); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int getpagesize() {
+ return mlibc::page_size;
+}
+
+// Code taken from musl
+// GLIBC extension for stdin/stdout
+char *getpass(const char *prompt) {
+ int fdin, fdout;
+ struct termios s, t;
+ ssize_t l;
+ static char password[128];
+
+ if((fdin = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0) {
+ fdin = STDIN_FILENO;
+ fdout = STDOUT_FILENO;
+ } else {
+ fdout = fdin;
+ }
+
+ tcgetattr(fdin, &t);
+ s = t;
+ t.c_lflag &= ~(ECHO | ISIG);
+ t.c_lflag |= ICANON;
+ t.c_iflag &= ~(INLCR | IGNCR);
+ t.c_iflag |= ICRNL;
+ tcsetattr(fdin, TCSAFLUSH, &t);
+ tcdrain(fdin);
+
+ dprintf(fdout, "%s", prompt);
+
+ l = read(fdin, password, sizeof password);
+ if(l >= 0) {
+ if((l > 0 && password[l - 1] == '\n') || l == sizeof password)
+ l--;
+ password[l] = 0;
+ }
+
+ tcsetattr(fdin, TCSAFLUSH, &s);
+
+ dprintf(fdout, "\n");
+ if(fdin != STDIN_FILENO) {
+ close(fdin);
+ }
+
+ return l < 0 ? 0 : password;
+}
+
+char *get_current_dir_name(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// This is a Linux extension
+pid_t gettid(void) {
+ if(!mlibc::sys_gettid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_gettid()");
+ }
+ return mlibc::sys_gettid();
+}
+
+int getentropy(void *buffer, size_t length) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getentropy, -1);
+ if(length > 256) {
+ errno = EIO;
+ return -1;
+ }
+ if(int e = mlibc::sys_getentropy(buffer, length); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+ssize_t write(int fd, const void *buf, size_t count) {
+ ssize_t bytes_written;
+ if(int e = mlibc::sys_write(fd, buf, count, &bytes_written); e) {
+ errno = e;
+ return (ssize_t)-1;
+ }
+ return bytes_written;
+}
+
+ssize_t read(int fd, void *buf, size_t count) {
+ ssize_t bytes_read;
+ if(int e = mlibc::sys_read(fd, buf, count, &bytes_read); e) {
+ errno = e;
+ return (ssize_t)-1;
+ }
+ return bytes_read;
+}
+
+off_t lseek(int fd, off_t offset, int whence) {
+ off_t new_offset;
+ if(int e = mlibc::sys_seek(fd, offset, whence, &new_offset); e) {
+ errno = e;
+ return (off_t)-1;
+ }
+ return new_offset;
+}
+
+off64_t lseek64(int fd, off64_t offset, int whence) {
+ off64_t new_offset;
+ if(int e = mlibc::sys_seek(fd, offset, whence, &new_offset); e) {
+ errno = e;
+ return (off64_t)-1;
+ }
+ return new_offset;
+}
+
+int close(int fd) {
+ return mlibc::sys_close(fd);
+}
+
+unsigned int sleep(unsigned int secs) {
+ time_t seconds = secs;
+ long nanos = 0;
+ if(!mlibc::sys_sleep) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_sleep()");
+ }
+ mlibc::sys_sleep(&seconds, &nanos);
+ return seconds;
+}
+
+// In contrast to sleep() this functions returns 0/-1 on success/failure.
+int usleep(useconds_t usecs) {
+ time_t seconds = 0;
+ long nanos = usecs * 1000;
+ if(!mlibc::sys_sleep) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_sleep()");
+ }
+ return mlibc::sys_sleep(&seconds, &nanos);
+}
+
+int dup(int fd) {
+ int newfd;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_dup, -1);
+ if(int e = mlibc::sys_dup(fd, 0, &newfd); e) {
+ errno = e;
+ return -1;
+ }
+ return newfd;
+}
+
+int dup2(int fd, int newfd) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_dup2, -1);
+ if(int e = mlibc::sys_dup2(fd, 0, newfd); e) {
+ errno = e;
+ return -1;
+ }
+ return newfd;
+}
+
+pid_t fork(void) {
+ auto self = mlibc::get_current_tcb();
+ pid_t child;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork, -1);
+
+ auto hand = self->atforkEnd;
+ while (hand) {
+ if (hand->prepare)
+ hand->prepare();
+
+ hand = hand->prev;
+ }
+
+ if(int e = mlibc::sys_fork(&child); e) {
+ errno = e;
+ return -1;
+ }
+
+ hand = self->atforkBegin;
+ while (hand) {
+ if (!child) {
+ if (hand->child)
+ hand->child();
+ } else {
+ if (hand->parent)
+ hand->parent();
+ }
+ hand = hand->next;
+ }
+
+ return child;
+}
+
+pid_t vfork(void) {
+ pid_t child;
+ /*
+ * Fork handlers established using pthread_atfork(3) are not
+ * called when a multithreaded program employing the NPTL
+ * threading library calls vfork(). Fork handlers are called
+ * in this case in a program using the LinuxThreads threading
+ * library. (See pthreads(7) for a description of Linux
+ * threading libraries.)
+ * - vfork(2), release 5.13 of the Linux man-pages project
+ *
+ * as a result, we call sys_fork instead of running atforks
+ */
+
+ /* deferring to fork as implementing vfork correctly requires assembly
+ * to handle not mucking up the stack
+ */
+ if(!mlibc::sys_fork) {
+ MLIBC_MISSING_SYSDEP();
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if(int e = mlibc::sys_fork(&child); e) {
+ errno = e;
+ return -1;
+ }
+
+ return child;
+}
+
+int execve(const char *path, char *const argv[], char *const envp[]) {
+ char *null_list[] = {
+ nullptr
+ };
+
+ if(!argv)
+ argv = null_list;
+ if(!envp)
+ envp = null_list;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_execve, -1);
+ int e = mlibc::sys_execve(path, argv, envp);
+ __ensure(e && "sys_execve() is expected to fail if it returns");
+ errno = e;
+ return -1;
+}
+
+gid_t getgid(void) {
+ if(!mlibc::sys_getgid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_getgid()");
+ }
+ return mlibc::sys_getgid();
+}
+
+gid_t getegid(void) {
+ if(!mlibc::sys_getegid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_getegid()");
+ }
+ return mlibc::sys_getegid();
+}
+
+uid_t getuid(void) {
+ if(!mlibc::sys_getuid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_getuid()");
+ }
+ return mlibc::sys_getuid();
+}
+
+uid_t geteuid(void) {
+ if(!mlibc::sys_geteuid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_geteuid()");
+ }
+ return mlibc::sys_geteuid();
+}
+
+pid_t getpid(void) {
+ if(!mlibc::sys_getpid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_getpid()");
+ }
+ return mlibc::sys_getpid();
+}
+
+pid_t getppid(void) {
+ if(!mlibc::sys_getppid) {
+ MLIBC_MISSING_SYSDEP();
+ __ensure(!"Cannot continue without sys_getppid()");
+ }
+ return mlibc::sys_getppid();
+}
+
+int access(const char *path, int mode) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_access, -1);
+ if(int e = mlibc::sys_access(path, mode); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+char *getusershell(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void setusershell(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void endusershell(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int isatty(int fd) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_isatty, 0);
+ if(int e = mlibc::sys_isatty(fd); e) {
+ errno = e;
+ return 0;
+ }
+ return 1;
+}
+
+int chroot(const char *ptr) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_chroot, -1);
+ if(int e = mlibc::sys_chroot(ptr); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int daemon(int, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+char *ctermid(char *s) {
+ return s ? strcpy(s, "/dev/tty") : const_cast<char *>("/dev/tty");
+}
+
+int setresuid(uid_t ruid, uid_t euid, uid_t suid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setresuid, -1);
+ if(int e = mlibc::sys_setresuid(ruid, euid, suid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int setresgid(gid_t rgid, gid_t egid, gid_t sgid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_setresgid, -1);
+ if(int e = mlibc::sys_setresgid(rgid, egid, sgid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int getdomainname(char *, size_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int setdomainname(const char *, size_t) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getresuid, -1);
+ if(int e = mlibc::sys_getresuid(ruid, euid, suid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_getresgid, -1);
+ if(int e = mlibc::sys_getresgid(rgid, egid, sgid); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+#if __MLIBC_CRYPT_OPTION
+void encrypt(char[64], int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+#endif
+
+#if __MLIBC_BSD_OPTION
+void *sbrk(intptr_t increment) {
+ if(increment) {
+ errno = ENOMEM;
+ return (void *)-1;
+ }
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_brk, (void *)-1);
+ void *out;
+ if(int e = mlibc::sys_brk(&out); e) {
+ errno = e;
+ return (void *)-1;
+ }
+ return out;
+}
+#endif
diff --git a/lib/mlibc/options/posix/generic/utime-stubs.cpp b/lib/mlibc/options/posix/generic/utime-stubs.cpp
new file mode 100644
index 0000000..f78729f
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/utime-stubs.cpp
@@ -0,0 +1,31 @@
+
+#include <utime.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <bits/ensure.h>
+#include <mlibc/posix-sysdeps.hpp>
+
+int utime(const char *filename, const struct utimbuf *times) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_utimensat, -1);
+ struct timespec time[2];
+ if(times) {
+ time[0].tv_sec = times->actime;
+ time[0].tv_nsec = 0;
+ time[1].tv_sec = times->modtime;
+ time[1].tv_nsec = 0;
+ } else {
+ time[0].tv_sec = UTIME_NOW;
+ time[0].tv_nsec = UTIME_NOW;
+ time[1].tv_sec = UTIME_NOW;
+ time[1].tv_nsec = UTIME_NOW;
+ }
+
+ if (int e = mlibc::sys_utimensat(AT_FDCWD, filename, time, 0); e) {
+ errno = e;
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/lib/mlibc/options/posix/generic/wordexp-stubs.cpp b/lib/mlibc/options/posix/generic/wordexp-stubs.cpp
new file mode 100644
index 0000000..8443527
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/wordexp-stubs.cpp
@@ -0,0 +1,342 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * code taken from OPNSense, with modifications
+ *
+ * Copyright (c) 2002 Tim J. Robbins.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <wordexp.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdint.h>
+
+#define SHELL_PATH "/bin/sh"
+#define SHELL_NAME "sh"
+
+static size_t we_read_fully(int fd, char *buffer, size_t len) {
+ size_t done = 0;
+
+ do {
+ ssize_t nread = read(fd, buffer + done, len - done);
+ if(nread == -1 && errno == EINTR)
+ continue;
+ if(nread <= 0)
+ break;
+ done += nread;
+ } while (done != len);
+
+ return done;
+}
+
+static int we_askshell(const char *words, wordexp_t *we, int flags) {
+ int pdes[2]; /* pipe to child */
+ char bbuf[9]; /* buffer for byte count */
+ char wbuf[9]; /* buffer for word count */
+ size_t nwords = 0; /* number of words from child */
+ size_t nbytes = 0; /* number of bytes from child */
+ size_t sofs = 0; /* offset into we->we_strings */
+ size_t vofs = 0; /* offset into we->we_wordv */
+ pid_t pid; /* PID of child */
+ pid_t wpid; /* waitpid return value */
+ int status; /* child exit status */
+ int error; /* our return value */
+ int serrno; /* errno to return */
+ char *np, *p; /* handy pointers */
+ char *nstrings; /* temporary for realloc() */
+ char **new_wordv; /* temporary for realloc() */
+ sigset_t newsigblock;
+ sigset_t oldsigblock;
+ const char *ifs = getenv("IFS");
+
+ serrno = errno;
+
+ if(pipe2(pdes, O_CLOEXEC) < 0)
+ return WRDE_NOSPACE;
+
+ (void)sigemptyset(&newsigblock);
+ (void)sigaddset(&newsigblock, SIGCHLD);
+ (void)sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
+
+ if((pid = fork()) < 0) {
+ serrno = errno;
+ close(pdes[0]);
+ close(pdes[1]);
+ (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+ errno = serrno;
+ return WRDE_NOSPACE;
+ } else if(pid == 0) {
+ /*
+ * We are the child; make /bin/sh expand `words'.
+ */
+ (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+ if((pdes[1] != STDOUT_FILENO ? dup2(pdes[1], STDOUT_FILENO) : fcntl(pdes[1], F_SETFD, 0)) < 0)
+ _exit(1);
+
+ execl(SHELL_PATH, SHELL_NAME, flags & WRDE_UNDEF ? "-u" : "+u",
+ "-c", "IFS=$1;eval \"$2\";eval \"set -- $3\";IFS=;a=\"$*\";"
+ "printf '%08x' \"$#\" \"${#a}\";printf '%s\\0' \"$@\"", "",
+ ifs != NULL ? ifs : " \t\n",
+ flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null",
+ words,
+ (char *)NULL);
+ _exit(1);
+ }
+
+ /*
+ * We are the parent; read the output of the shell wordexp function,
+ * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal
+ * byte count (not including terminating null bytes), followed by
+ * the expanded words separated by nulls.
+ */
+ close(pdes[1]);
+ if(we_read_fully(pdes[0], wbuf, 8) != 8 || we_read_fully(pdes[0], bbuf, 8) != 8) {
+ error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
+ serrno = errno;
+ goto cleanup;
+ }
+ wbuf[8] = bbuf[8] = '\0';
+ nwords = strtol(wbuf, NULL, 16);
+ nbytes = strtol(bbuf, NULL, 16) + nwords;
+
+ /*
+ * Allocate or reallocate (when flags & WRDE_APPEND) the word vector
+ * and string storage buffers for the expanded words we're about to
+ * read from the child.
+ */
+ sofs = we->we_nbytes;
+ vofs = we->we_wordc;
+ if((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS | WRDE_APPEND))
+ vofs += we->we_offs;
+ we->we_wordc += nwords;
+ we->we_nbytes += nbytes;
+
+ if((new_wordv = (char **) realloc(we->we_wordv, (we->we_wordc + 1 + (flags & WRDE_DOOFFS ? we->we_offs : 0)) * sizeof(char *))) == NULL) {
+ error = WRDE_NOSPACE;
+ goto cleanup;
+ }
+
+ we->we_wordv = new_wordv;
+
+ if((nstrings = (char *) realloc(we->we_strings, we->we_nbytes)) == NULL) {
+ error = WRDE_NOSPACE;
+ goto cleanup;
+ }
+
+ for(size_t i = 0; i < vofs; i++) {
+ if(we->we_wordv[i] != NULL)
+ we->we_wordv[i] += nstrings - we->we_strings;
+ }
+ we->we_strings = nstrings;
+
+ if(we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
+ error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
+ serrno = errno;
+ goto cleanup;
+ }
+
+ error = 0;
+cleanup:
+ close(pdes[0]);
+
+ do {
+ wpid = waitpid(pid, &status, 0);
+ } while(wpid < 0 && errno == EINTR);
+
+ (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+
+ if(error != 0) {
+ errno = serrno;
+ return error;
+ }
+
+ if(wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ return flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
+
+ /*
+ * Break the null-terminated expanded word strings out into
+ * the vector.
+ */
+ if(vofs == 0 && flags & WRDE_DOOFFS) {
+ while (vofs < we->we_offs)
+ we->we_wordv[vofs++] = NULL;
+ }
+
+ p = we->we_strings + sofs;
+ while (nwords-- != 0) {
+ we->we_wordv[vofs++] = p;
+ if((np = (char *) memchr(p, '\0', nbytes)) == NULL)
+ return WRDE_NOSPACE;
+
+ nbytes -= np - p + 1;
+ p = np + 1;
+ }
+
+ we->we_wordv[vofs] = NULL;
+ return 0;
+}
+
+/*
+ * we_check --
+ * Check that the string contains none of the following unquoted
+ * special characters: <newline> |&;<>(){}
+ * or command substitutions when WRDE_NOCMD is set in flags.
+ */
+static int we_check(const char *words, int flags)
+{
+ char c;
+ int dquote, level, quote, squote;
+
+ quote = squote = dquote = 0;
+ while ((c = *words++) != '\0') {
+ switch (c) {
+ case '\\': {
+ if(squote == 0)
+ quote ^= 1;
+ continue;
+ }
+ case '\'': {
+ if(quote + dquote == 0)
+ squote ^= 1;
+ break;
+ }
+ case '"': {
+ if(quote + squote == 0)
+ dquote ^= 1;
+ break;
+ }
+ case '`': {
+ if(quote + squote == 0 && flags & WRDE_NOCMD)
+ return WRDE_CMDSUB;
+ while ((c = *words++) != '\0' && c != '`')
+ if(c == '\\' && (c = *words++) == '\0')
+ break;
+ if(c == '\0')
+ return WRDE_SYNTAX;
+ break;
+ }
+ case '|':
+ case '&':
+ case ';':
+ case '<':
+ case '>':
+ case '{':
+ case '}':
+ case '(':
+ case ')':
+ case '\n': {
+ if(quote + squote + dquote == 0)
+ return WRDE_BADCHAR;
+ break;
+ }
+ case '$': {
+ if((c = *words++) == '\0')
+ break;
+ else if(quote + squote == 0 && c == '(') {
+ if(flags & WRDE_NOCMD && *words != '(')
+ return WRDE_CMDSUB;
+ level = 1;
+ while ((c = *words++) != '\0') {
+ if(c == '\\') {
+ if((c = *words++) == '\0')
+ break;
+ } else if(c == '(')
+ level++;
+ else if(c == ')' && --level == 0)
+ break;
+ }
+ if(c == '\0' || level != 0)
+ return WRDE_SYNTAX;
+ } else if(quote + squote == 0 && c == '{') {
+ level = 1;
+ while ((c = *words++) != '\0') {
+ if(c == '\\') {
+ if((c = *words++) == '\0')
+ break;
+ } else if(c == '{')
+ level++;
+ else if(c == '}' && --level == 0)
+ break;
+ }
+ if(c == '\0' || level != 0)
+ return WRDE_SYNTAX;
+ } else
+ --words;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ quote = 0;
+ }
+
+ if(quote + squote + dquote != 0)
+ return WRDE_SYNTAX;
+
+ return 0;
+}
+
+int wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) {
+ int error;
+
+ if(flags & WRDE_REUSE)
+ wordfree(we);
+
+ if((flags & WRDE_APPEND) == 0) {
+ we->we_wordc = 0;
+ we->we_wordv = NULL;
+ we->we_strings = NULL;
+ we->we_nbytes = 0;
+ }
+
+ if((error = we_check(words, flags)) != 0) {
+ wordfree(we);
+ return error;
+ }
+
+ if((error = we_askshell(words, we, flags)) != 0) {
+ wordfree(we);
+ return error;
+ }
+
+ return 0;
+}
+
+void wordfree(wordexp_t *we) {
+ if (we == NULL)
+ return;
+ free(we->we_wordv);
+ free(we->we_strings);
+ we->we_wordv = NULL;
+ we->we_strings = NULL;
+ we->we_nbytes = 0;
+ we->we_wordc = 0;
+}
diff --git a/lib/mlibc/options/posix/include/arpa/inet.h b/lib/mlibc/options/posix/include/arpa/inet.h
new file mode 100644
index 0000000..599987e
--- /dev/null
+++ b/lib/mlibc/options/posix/include/arpa/inet.h
@@ -0,0 +1,46 @@
+#ifndef _ARPA_INET_H
+#define _ARPA_INET_H
+
+#include <netinet/in.h>
+#include <stdint.h>
+#include <sys/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+uint32_t htonl(uint32_t);
+uint16_t htons(uint16_t);
+uint32_t ntohl(uint32_t);
+uint16_t ntohs(uint16_t);
+
+// ----------------------------------------------------------------------------
+// IPv4 address manipulation.
+// ----------------------------------------------------------------------------
+
+in_addr_t inet_addr(const char *);
+char *inet_ntoa(struct in_addr);
+
+// GLIBC replacement for inet_addr().
+int inet_aton(const char *, struct in_addr *);
+
+// ----------------------------------------------------------------------------
+// Generic IP address manipulation.
+// ----------------------------------------------------------------------------
+const char *inet_ntop(int, const void *__restrict, char *__restrict,
+ socklen_t) __attribute__((__nonnull__(3)));
+int inet_pton(int, const char *__restrict, void *__restrict);
+
+struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
+in_addr_t inet_netof(struct in_addr in);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _ARPA_INET_H
+
diff --git a/lib/mlibc/options/posix/include/bits/posix/fd_set.h b/lib/mlibc/options/posix/include/bits/posix/fd_set.h
new file mode 100644
index 0000000..554738e
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/fd_set.h
@@ -0,0 +1,14 @@
+#ifndef MLIBC_FD_SET_H
+#define MLIBC_FD_SET_H
+
+#include <bits/types.h>
+
+typedef struct {
+ union {
+ __mlibc_uint8 __mlibc_elems[128];
+ // Some programs require the fds_bits field to be present
+ __mlibc_uint8 fds_bits[128];
+ };
+} fd_set;
+
+#endif // MLIBC_FD_SET_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/id_t.h b/lib/mlibc/options/posix/include/bits/posix/id_t.h
new file mode 100644
index 0000000..f222bc1
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/id_t.h
@@ -0,0 +1,6 @@
+#ifndef _MLIBC_ID_T_H
+#define _MLIBC_ID_T_H
+
+typedef unsigned int id_t;
+
+#endif // _MLIBC_ID_T_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/in_addr_t.h b/lib/mlibc/options/posix/include/bits/posix/in_addr_t.h
new file mode 100644
index 0000000..bac9ff2
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/in_addr_t.h
@@ -0,0 +1,8 @@
+#ifndef MLIBC_IN_ADDR_H
+#define MLIBC_IN_ADDR_H
+
+#include <stdint.h>
+
+typedef uint32_t in_addr_t;
+
+#endif // MLIBC_IN_ADDR_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/in_port_t.h b/lib/mlibc/options/posix/include/bits/posix/in_port_t.h
new file mode 100644
index 0000000..0368159
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/in_port_t.h
@@ -0,0 +1,8 @@
+#ifndef MLIBC_IN_PORT_H
+#define MLIBC_IN_PORT_H
+
+#include <stdint.h>
+
+typedef uint16_t in_port_t;
+
+#endif // MLIBC_IN_PORT_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/iovec.h b/lib/mlibc/options/posix/include/bits/posix/iovec.h
new file mode 100644
index 0000000..c004f1c
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/iovec.h
@@ -0,0 +1,11 @@
+#ifndef MLIBC_IOVEC_H
+#define MLIBC_IOVEC_H
+
+#include <bits/types.h>
+
+struct iovec {
+ void *iov_base;
+ __mlibc_size iov_len;
+};
+
+#endif // MLIBC_IOVEC_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/locale_t.h b/lib/mlibc/options/posix/include/bits/posix/locale_t.h
new file mode 100644
index 0000000..c128d58
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/locale_t.h
@@ -0,0 +1,14 @@
+#ifndef _LOCALE_T_H
+#define _LOCALE_T_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *locale_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LOCALE_T_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_ctype.h b/lib/mlibc/options/posix/include/bits/posix/posix_ctype.h
new file mode 100644
index 0000000..2b11057
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_ctype.h
@@ -0,0 +1,36 @@
+#ifndef _POSIX_CTYPE_H
+#define _POSIX_CTYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/posix/locale_t.h>
+
+#ifndef __MLIBC_ABI_ONLY
+
+int isalnum_l(int c, locale_t loc);
+int isalpha_l(int c, locale_t loc);
+int isblank_l(int c, locale_t loc);
+int iscntrl_l(int c, locale_t loc);
+int isdigit_l(int c, locale_t loc);
+int isgraph_l(int c, locale_t loc);
+int islower_l(int c, locale_t loc);
+int isprint_l(int c, locale_t loc);
+int ispunct_l(int c, locale_t loc);
+int isspace_l(int c, locale_t loc);
+int isupper_l(int c, locale_t loc);
+int isxdigit_l(int c, locale_t loc);
+
+int isascii_l(int c, locale_t loc);
+
+int tolower_l(int c, locale_t loc);
+int toupper_l(int c, locale_t loc);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _POSIX_CTYPE_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_locale.h b/lib/mlibc/options/posix/include/bits/posix/posix_locale.h
new file mode 100644
index 0000000..7554bc5
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_locale.h
@@ -0,0 +1,23 @@
+#ifndef MLIBC_POSIX_LOCALE_H
+#define MLIBC_POSIX_LOCALE_H
+
+#include <bits/posix/locale_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+locale_t newlocale(int category_mask, const char *locale, locale_t base);
+void freelocale(locale_t locobj);
+locale_t uselocale(locale_t locobj);
+locale_t duplocale(locale_t locobj);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_POSIX_LOCALE_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_signal.h b/lib/mlibc/options/posix/include/bits/posix/posix_signal.h
new file mode 100644
index 0000000..20f030f
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_signal.h
@@ -0,0 +1,111 @@
+
+#ifndef MLIBC_POSIX_SIGNAL_H
+#define MLIBC_POSIX_SIGNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/ansi/time_t.h>
+#include <bits/ansi/timespec.h>
+#include <bits/posix/pthread_t.h>
+#include <bits/sigset_t.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <mlibc-config.h>
+
+#define FPE_INTDIV 1 /* integer divide by zero */
+#define FPE_INTOVF 2 /* integer overflow */
+#define FPE_FLTDIV 3 /* floating point divide by zero */
+#define FPE_FLTOVF 4 /* floating point overflow */
+#define FPE_FLTUND 5 /* floating point underflow */
+#define FPE_FLTRES 6 /* floating point inexact result */
+#define FPE_FLTINV 7 /* floating point invalid operation */
+#define FPE_FLTSUB 8 /* subscript out of range */
+
+#define TRAP_BRKPT 1 /* process breakpoint */
+#define TRAP_TRACE 2 /* process trace trap */
+
+// Start Glibc stuff
+
+struct _libc_fpxreg {
+ unsigned short int significand[4];
+ unsigned short int exponent;
+ unsigned short int __glibc_reserved1[3];
+};
+
+struct _libc_xmmreg {
+ uint32_t element[4];
+};
+
+struct _libc_fpstate {
+ uint16_t cwd;
+ int16_t swd;
+ uint16_t ftw;
+ uint16_t fop;
+ uint64_t rip;
+ uint64_t dp;
+ uint32_t mxcsr;
+ uint32_t mxcr_mask;
+ struct _libc_fpxreg _st[8];
+ struct _libc_xmmreg _xmm[16];
+ uint32_t __glibc_reserved1[24];
+};
+
+typedef struct _libc_fpstate *fpregset_t;
+// End Glibc stuff
+
+typedef unsigned long int greg_t;
+
+#define FPE_INTDIV 1 /* integer divide by zero */
+#define FPE_INTOVF 2 /* integer overflow */
+#define FPE_FLTDIV 3 /* floating point divide by zero */
+#define FPE_FLTOVF 4 /* floating point overflow */
+#define FPE_FLTUND 5 /* floating point underflow */
+#define FPE_FLTRES 6 /* floating point inexact result */
+#define FPE_FLTINV 7 /* floating point invalid operation */
+#define FPE_FLTSUB 8 /* subscript out of range */
+
+#define TRAP_BRKPT 1 /* process breakpoint */
+#define TRAP_TRACE 2 /* process trace trap */
+
+#ifndef __MLIBC_ABI_ONLY
+
+// functions to block / wait for signals
+int sigsuspend(const sigset_t *);
+int sigprocmask(int, const sigset_t *__restrict, sigset_t *__restrict);
+
+int pthread_sigmask(int, const sigset_t *__restrict, sigset_t *__restrict);
+int pthread_kill(pthread_t, int);
+
+// functions to handle signals
+int sigaction(int, const struct sigaction *__restrict, struct sigaction *__restrict);
+int sigpending(sigset_t *);
+
+int siginterrupt(int sig, int flag);
+
+int sigaltstack(const stack_t *__restrict ss, stack_t *__restrict oss);
+
+// functions to raise signals
+int kill(pid_t, int);
+int killpg(int, int);
+
+int sigtimedwait(const sigset_t *__restrict set, siginfo_t *__restrict info, const struct timespec *__restrict timeout);
+int sigwait(const sigset_t *__restrict set, int *__restrict sig);
+int sigwaitinfo(const sigset_t *__restrict set, siginfo_t *__restrict info);
+
+// Glibc extension
+#if __MLIBC_GLIBC_OPTION
+int sigisemptyset(const sigset_t *set);
+#endif // __MLIBC_GLIBC_OPTION
+
+int sigqueue(pid_t pid, int sig, const union sigval value);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_POSIX_SIGNAL_H
+
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_stdio.h b/lib/mlibc/options/posix/include/bits/posix/posix_stdio.h
new file mode 100644
index 0000000..4572a04
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_stdio.h
@@ -0,0 +1,72 @@
+
+#ifndef MLIBC_POSIX_STDIO_H
+#define MLIBC_POSIX_STDIO_H
+
+#include <bits/off_t.h>
+#include <bits/size_t.h>
+#include <bits/ssize_t.h>
+
+// MISSING: var_list
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define P_tmpdir "/tmp"
+
+#ifndef __MLIBC_ABI_ONLY
+
+typedef struct __mlibc_file_base FILE;
+
+int fileno(FILE *file);
+FILE *fdopen(int fd, const char *mode);
+
+FILE *fmemopen(void *__restrict, size_t, const char *__restrict);
+int pclose(FILE *);
+FILE *popen(const char*, const char *);
+FILE *open_memstream(char **, size_t *);
+
+int fseeko(FILE *stream, off_t offset, int whence);
+off_t ftello(FILE *stream);
+
+int dprintf(int fd, const char *format, ...);
+int vdprintf(int fd, const char *format, __builtin_va_list args);
+
+char *fgetln(FILE *, size_t *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#define RENAME_EXCHANGE (1 << 1)
+
+// GNU extensions
+typedef ssize_t (cookie_read_function_t)(void *, char *, size_t);
+typedef ssize_t (cookie_write_function_t)(void *, const char *, size_t);
+typedef int (cookie_seek_function_t)(void *, off_t *, int);
+typedef int (cookie_close_function_t)(void *);
+
+typedef struct _IO_cookie_io_functions_t {
+ cookie_read_function_t *read;
+ cookie_write_function_t *write;
+ cookie_seek_function_t *seek;
+ cookie_close_function_t *close;
+} cookie_io_functions_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+#if defined(_GNU_SOURCE)
+
+FILE *fopencookie(void *__restrict cookie, const char *__restrict mode, cookie_io_functions_t io_funcs);
+
+#endif // defined(_GNU_SOURCE)
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+// MISSING: various functions and macros
+
+#endif /* MLIBC_POSIX_STDIO_H */
+
+
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_stdlib.h b/lib/mlibc/options/posix/include/bits/posix/posix_stdlib.h
new file mode 100644
index 0000000..5248fca
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_stdlib.h
@@ -0,0 +1,73 @@
+
+#ifndef MLIBC_POSIX_STDLIB_H
+#define MLIBC_POSIX_STDLIB_H
+
+#include <bits/posix/locale_t.h>
+#include <bits/size_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+long random(void);
+double drand48(void);
+void srand48(long int);
+char *initstate(unsigned int, char *, size_t);
+char *setstate(char *);
+void srandom(unsigned int);
+
+// ----------------------------------------------------------------------------
+// Environment.
+// ----------------------------------------------------------------------------
+
+int putenv(char *);
+int setenv(const char *, const char *, int);
+int unsetenv(const char *);
+
+// ----------------------------------------------------------------------------
+// Path handling.
+// ----------------------------------------------------------------------------
+
+int mkstemp(char *);
+int mkstemps(char *pattern, int suffixlen);
+int mkostemp(char *, int flags);
+int mkostemps(char *pattern, int suffixlen, int flags);
+char *mkdtemp(char *path);
+
+char *realpath(const char *__restrict, char *__restrict);
+
+// ----------------------------------------------------------------------------
+// Pseudoterminals
+// ----------------------------------------------------------------------------
+
+int posix_openpt(int flags);
+int grantpt(int fd);
+int unlockpt(int fd);
+char *ptsname(int fd);
+int ptsname_r(int fd, char *buf, size_t len);
+
+double strtod_l(const char *__restrict__ nptr, char ** __restrict__ endptr, locale_t loc);
+long double strtold_l(const char *__restrict__ nptr, char ** __restrict__ endptr, locale_t loc);
+float strtof_l(const char *__restrict string, char **__restrict end, locale_t loc);
+
+int getloadavg(double *, int);
+
+// GNU extension
+char *secure_getenv(const char *);
+char *canonicalize_file_name(const char *);
+
+// BSD extension
+void *reallocarray(void *, size_t, size_t);
+
+int clearenv(void);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_POSIX_STDLIB_H
+
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_string.h b/lib/mlibc/options/posix/include/bits/posix/posix_string.h
new file mode 100644
index 0000000..1f61942
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_string.h
@@ -0,0 +1,57 @@
+
+#ifndef MLIBC_POSIX_STRING_H
+#define MLIBC_POSIX_STRING_H
+
+#include <alloca.h>
+#include <bits/posix/locale_t.h>
+#include <bits/size_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+char *strdup(const char *string);
+char *strndup(const char *, size_t);
+size_t strnlen(const char *, size_t);
+char *strtok_r(char *__restrict, const char *__restrict, char **__restrict);
+char *strsep(char **stringp, const char *delim);
+char *strsignal(int sig);
+char *stpcpy(char *__restrict, const char *__restrict);
+char *stpncpy(char *__restrict, const char *__restrict, size_t n);
+void *memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n);
+
+int strcoll_l(const char *s1, const char *s2, locale_t locale);
+
+// GNU extensions.
+#if defined(_GNU_SOURCE)
+char *strcasestr(const char *, const char *);
+#define strdupa(x) ({ \
+ const char *str = (x); \
+ size_t len = strlen(str) + 1; \
+ char *buf = alloca(len); \
+ (char *) memcpy(buf, str, len); \
+})
+#define strndupa(x, y) ({ \
+ const char *str = (x); \
+ size_t len = strnlen(str, (y)) + 1; \
+ char *buf = alloca(len); \
+ buf[len - 1] = '\0'; \
+ (char *) memcpy(buf, str, len - 1); \
+})
+void *memrchr(const void *, int, size_t);
+#endif /* defined(_GNU_SOURCE) */
+
+// BSD extensions
+size_t strlcpy(char *d, const char *s, size_t n);
+size_t strlcat(char *d, const char *s, size_t n);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_POSIX_STRING_H
+
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_time.h b/lib/mlibc/options/posix/include/bits/posix/posix_time.h
new file mode 100644
index 0000000..d3e8e1d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_time.h
@@ -0,0 +1,25 @@
+#ifndef MLIBC_POSIX_TIME_H
+#define MLIBC_POSIX_TIME_H
+
+#include <bits/posix/timeval.h>
+
+#define TIMER_ABSTIME 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int utimes(const char *, const struct timeval[2]);
+
+// Not standardized, Linux and BSDs have it
+int futimes(int, const struct timeval[2]);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MLIBC_POSIX_TIME_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/posix_wctype.h b/lib/mlibc/options/posix/include/bits/posix/posix_wctype.h
new file mode 100644
index 0000000..4d2887c
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/posix_wctype.h
@@ -0,0 +1,44 @@
+#ifndef _POSIX_WCTYPE_H
+#define _POSIX_WCTYPE_H
+
+#include <bits/posix/locale_t.h>
+#include <bits/wint_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+typedef unsigned long wctype_t;
+typedef unsigned long wctrans_t;
+
+int iswalnum_l(wint_t, locale_t);
+int iswblank_l(wint_t, locale_t);
+int iswcntrl_l(wint_t, locale_t);
+int iswdigit_l(wint_t, locale_t);
+int iswgraph_l(wint_t, locale_t);
+int iswlower_l(wint_t, locale_t);
+int iswprint_l(wint_t, locale_t);
+int iswpunct_l(wint_t, locale_t);
+int iswspace_l(wint_t, locale_t);
+int iswupper_l(wint_t, locale_t);
+int iswxdigit_l(wint_t, locale_t);
+int iswalpha_l(wint_t, locale_t);
+
+wctype_t wctype_l(const char *);
+int iswctype_l(wint_t, wctype_t);
+
+wint_t towlower_l(wint_t, locale_t);
+wint_t towupper_l(wint_t, locale_t);
+
+wctrans_t wctrans_l(const char *, locale_t);
+wint_t towctrans_l(wint_t, wctrans_t, locale_t);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _POSIX_WCTYPE_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/pthread_t.h b/lib/mlibc/options/posix/include/bits/posix/pthread_t.h
new file mode 100644
index 0000000..1310c40
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/pthread_t.h
@@ -0,0 +1,8 @@
+#ifndef _MLIBC_BITS_PTHREAD_T_HPP
+#define _MLIBC_BITS_PTHREAD_T_HPP
+
+#include <bits/threads.h>
+
+typedef struct __mlibc_thread_data *pthread_t;
+
+#endif // _MLIBC_BITS_PTHREAD_T_HPP
diff --git a/lib/mlibc/options/posix/include/bits/posix/stat.h b/lib/mlibc/options/posix/include/bits/posix/stat.h
new file mode 100644
index 0000000..4dd081d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/stat.h
@@ -0,0 +1,24 @@
+#ifndef MLIBC_STAT_H
+#define MLIBC_STAT_H
+
+#include <abi-bits/stat.h>
+
+// Used by utimensat and friends
+#define UTIME_NOW ((1l << 30) - 1l)
+#define UTIME_OMIT ((1l << 30) - 2l)
+
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+
+// POSIX compatibility macros
+#define st_atime st_atim.tv_sec
+#define st_mtime st_mtim.tv_sec
+#define st_ctime st_ctim.tv_sec
+
+#endif // MLIBC_STAT_H
+
diff --git a/lib/mlibc/options/posix/include/bits/posix/timer_t.h b/lib/mlibc/options/posix/include/bits/posix/timer_t.h
new file mode 100644
index 0000000..b230501
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/timer_t.h
@@ -0,0 +1,6 @@
+#ifndef _MLIBC_TIMER_T_H
+#define _MLIBC_TIMER_T_H
+
+typedef void * timer_t;
+
+#endif // _MLIBC_TIMER_T_H
diff --git a/lib/mlibc/options/posix/include/bits/posix/timeval.h b/lib/mlibc/options/posix/include/bits/posix/timeval.h
new file mode 100644
index 0000000..445ee7f
--- /dev/null
+++ b/lib/mlibc/options/posix/include/bits/posix/timeval.h
@@ -0,0 +1,12 @@
+#ifndef MLIBC_TIMEVAL_H
+#define MLIBC_TIMEVAL_H
+
+#include <bits/ansi/time_t.h>
+#include <abi-bits/suseconds_t.h>
+
+struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+};
+
+#endif // MLIBC_TIMEVAL_H
diff --git a/lib/mlibc/options/posix/include/byteswap.h b/lib/mlibc/options/posix/include/byteswap.h
new file mode 100644
index 0000000..74b9fe2
--- /dev/null
+++ b/lib/mlibc/options/posix/include/byteswap.h
@@ -0,0 +1,23 @@
+
+#ifndef _BYTESWAP_H
+#define _BYTESWAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define bswap_16(x) __builtin_bswap16(x)
+#define bswap_32(x) __builtin_bswap32(x)
+#define bswap_64(x) __builtin_bswap64(x)
+
+// Some programs like eudev call these functions instead
+#define __bswap_16(x) __builtin_bswap16(x)
+#define __bswap_32(x) __builtin_bswap32(x)
+#define __bswap_64(x) __builtin_bswap64(x)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _BYTESWAP_H
+
diff --git a/lib/mlibc/options/posix/include/dirent.h b/lib/mlibc/options/posix/include/dirent.h
new file mode 100644
index 0000000..b50566d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/dirent.h
@@ -0,0 +1,76 @@
+
+#ifndef _DIRENT_H
+#define _DIRENT_H
+
+#include <abi-bits/ino_t.h>
+#include <bits/off_t.h>
+#include <bits/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#define __MLIBC_DIRENT_BODY ino_t d_ino; \
+ off_t d_off; \
+ unsigned short d_reclen; \
+ unsigned char d_type; \
+ char d_name[1024];
+
+struct dirent {
+ __MLIBC_DIRENT_BODY
+};
+
+struct dirent64 {
+ __MLIBC_DIRENT_BODY
+};
+
+#define d_fileno d_ino
+
+#undef __MLIBC_DIRENT_BODY
+
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+
+struct __mlibc_dir_struct {
+ int __handle;
+ __mlibc_size __ent_next;
+ __mlibc_size __ent_limit;
+ char __ent_buffer[2048];
+ struct dirent __current;
+};
+
+typedef struct __mlibc_dir_struct DIR;
+
+#ifndef __MLIBC_ABI_ONLY
+
+int alphasort(const struct dirent **, const struct dirent **);
+int closedir(DIR *);
+int dirfd(DIR *);
+DIR *fdopendir(int);
+DIR *opendir(const char *);
+struct dirent *readdir(DIR *);
+int readdir_r(DIR *__restrict, struct dirent *__restrict, struct dirent **__restrict);
+void rewinddir(DIR *);
+int scandir(const char *, struct dirent ***, int (*)(const struct dirent *),
+ int (*)(const struct dirent **, const struct dirent **));
+void seekdir(DIR *, long);
+long telldir(DIR *);
+int versionsort(const struct dirent **, const struct dirent **);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _DIRENT_H
+
diff --git a/lib/mlibc/options/posix/include/dlfcn.h b/lib/mlibc/options/posix/include/dlfcn.h
new file mode 100644
index 0000000..3bb8a02
--- /dev/null
+++ b/lib/mlibc/options/posix/include/dlfcn.h
@@ -0,0 +1,52 @@
+
+#ifndef _DLFCN_H
+#define _DLFCN_H
+
+#define RTLD_LOCAL 0
+#define RTLD_NOW 1
+#define RTLD_GLOBAL 2
+#define RTLD_NOLOAD 4
+#define RTLD_NODELETE 8
+#define RTLD_DEEPBIND 16
+#define RTLD_LAZY 32
+
+#define RTLD_NEXT ((void *)-1)
+#define RTLD_DEFAULT ((void *)0)
+
+#define RTLD_DI_LINKMAP 2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int dlclose(void *);
+char *dlerror(void);
+void *dlopen(const char *, int);
+void *dlsym(void *__restrict, const char *__restrict);
+void *dlvsym(void *__restrict, const char *__restrict, const char *__restrict);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+//gnu extension
+typedef struct {
+ const char *dli_fname;
+ void *dli_fbase;
+ const char *dli_sname;
+ void *dli_saddr;
+} Dl_info;
+
+#ifndef __MLIBC_ABI_ONLY
+
+int dladdr(const void *, Dl_info *);
+int dlinfo(void *, int, void *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _DLFCN_H
+
diff --git a/lib/mlibc/options/posix/include/fcntl.h b/lib/mlibc/options/posix/include/fcntl.h
new file mode 100644
index 0000000..9983219
--- /dev/null
+++ b/lib/mlibc/options/posix/include/fcntl.h
@@ -0,0 +1,76 @@
+
+#ifndef _FCNTL_H
+#define _FCNTL_H
+
+#include <abi-bits/fcntl.h>
+#include <abi-bits/seek-whence.h>
+#include <abi-bits/mode_t.h>
+#include <abi-bits/pid_t.h>
+#include <bits/posix/iovec.h>
+#include <bits/off_t.h>
+#include <bits/ssize_t.h>
+#include <bits/size_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define O_NDELAY O_NONBLOCK
+
+struct flock {
+ short l_type;
+ short l_whence;
+ off_t l_start;
+ off_t l_len;
+ pid_t l_pid;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int creat(const char *, mode_t);
+int fallocate(int fd, int mode, off_t offset, off_t len);
+int fcntl(int fd, int command, ...);
+int open(const char *path, int flags, ...);
+int openat(int, const char *, int, ...);
+int posix_fadvise(int, off_t, off_t, int);
+int posix_fallocate(int, off_t, off_t);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+// This is a linux extension
+
+struct file_handle {
+ unsigned int handle_bytes;
+ int handle_type;
+ unsigned char f_handle[0];
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int name_to_handle_at(int, const char *, struct file_handle *, int *, int);
+int open_by_handle_at(int, struct file_handle *, int);
+
+ssize_t splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len, unsigned int flags);
+ssize_t vmsplice(int fd, const struct iovec *iov, size_t nr_segs, unsigned int flags);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#define SPLICE_F_MOVE 1
+#define SPLICE_F_NONBLOCK 2
+#define SPLICE_F_MORE 4
+#define SPLICE_F_GIFT 8
+
+#define AT_NO_AUTOMOUNT 0x800
+
+#define F_SETPIPE_SZ 1031
+#define F_GETPIPE_SZ 1032
+
+#define FALLOC_FL_KEEP_SIZE 1
+#define FALLOC_FL_PUNCH_HOLE 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _FCNTL_H
+
diff --git a/lib/mlibc/options/posix/include/fnmatch.h b/lib/mlibc/options/posix/include/fnmatch.h
new file mode 100644
index 0000000..3eccbd0
--- /dev/null
+++ b/lib/mlibc/options/posix/include/fnmatch.h
@@ -0,0 +1,33 @@
+
+#ifndef _FNMATCH_H
+#define _FNMATCH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// POSIX-defined fnmatch() flags.
+#define FNM_PATHNAME 0x1
+#define FNM_NOESCAPE 0x2
+#define FNM_PERIOD 0x4
+
+// GNU extensions for fnmatch() flags.
+#define FNM_LEADING_DIR 0x8
+#define FNM_CASEFOLD 0x10
+#define FNM_EXTMATCH 0x20
+
+// fnmatch() return values.
+#define FNM_NOMATCH 1
+
+#ifndef __MLIBC_ABI_ONLY
+
+int fnmatch(const char *, const char *, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _FNMATCH_H
+
diff --git a/lib/mlibc/options/posix/include/ftw.h b/lib/mlibc/options/posix/include/ftw.h
new file mode 100644
index 0000000..bde283d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/ftw.h
@@ -0,0 +1,43 @@
+
+#ifndef _FTW_H
+#define _FTW_H
+
+#include <bits/posix/stat.h>
+
+#define FTW_F 1
+#define FTW_D 2
+#define FTW_DNR 3
+#define FTW_DP 4
+#define FTW_NS 5
+#define FTW_SL 6
+#define FTW_SLN 7
+
+#define FTW_PHYS 1
+#define FTW_MOUNT 2
+#define FTW_DEPTH 4
+#define FTW_CHDIR 8
+
+#define FTW_CONTINUE 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct FTW {
+ int base;
+ int level;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int ftw(const char *, int (*)(const char *, const struct stat *, int), int);
+int nftw(const char *, int (*)(const char *, const struct stat *, int, struct FTW *), int, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _FTW_H
+
diff --git a/lib/mlibc/options/posix/include/glob.h b/lib/mlibc/options/posix/include/glob.h
new file mode 100644
index 0000000..2bf9e48
--- /dev/null
+++ b/lib/mlibc/options/posix/include/glob.h
@@ -0,0 +1,58 @@
+
+#ifndef _GLOB_H
+#define _GLOB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/size_t.h>
+
+#define GLOB_APPEND 0x01
+#define GLOB_DOOFFS 0x02
+#define GLOB_ERR 0x04
+#define GLOB_MARK 0x08
+#define GLOB_NOCHECK 0x10
+#define GLOB_NOESCAPE 0x20
+#define GLOB_NOSORT 0x40
+#define GLOB_PERIOD 0x80
+#define GLOB_TILDE 0x100
+#define GLOB_TILDE_CHECK 0x200
+#define GLOB_BRACE 0x400
+#define GLOB_NOMAGIC 0x800
+#define GLOB_ALTDIRFUNC 0x1000
+#define GLOB_ONLYDIR 0x2000
+#define GLOB_MAGCHAR 0x4000
+
+#define GLOB_ABORTED 1
+#define GLOB_NOMATCH 2
+#define GLOB_NOSPACE 3
+#define GLOB_NOSYS 4
+
+struct stat;
+typedef struct glob_t {
+ size_t gl_pathc;
+ char **gl_pathv;
+ size_t gl_offs;
+ int gl_flags;
+ void (*gl_closedir) (void *);
+ struct dirent *(*gl_readdir) (void *);
+ void *(*gl_opendir) (const char *);
+ int (*gl_lstat) (const char *__restrict, struct stat *__restrict);
+ int (*gl_stat) (const char *__restrict, struct stat *__restrict);
+} glob_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+int glob(const char *__restirct, int, int(*)(const char *, int), struct glob_t *__restrict);
+void globfree(struct glob_t *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _GLOB_H
+
+
diff --git a/lib/mlibc/options/posix/include/grp.h b/lib/mlibc/options/posix/include/grp.h
new file mode 100644
index 0000000..a50396e
--- /dev/null
+++ b/lib/mlibc/options/posix/include/grp.h
@@ -0,0 +1,43 @@
+#ifndef _GRP_H
+#define _GRP_H
+
+#include <stddef.h>
+#include <stdio.h>
+#include <abi-bits/gid_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct group {
+ char *gr_name;
+ char *gr_passwd;
+ gid_t gr_gid;
+ char **gr_mem;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+void endgrent(void);
+struct group *getgrent(void);
+struct group *getgrgid(gid_t);
+int getgrgid_r(gid_t, struct group *, char *, size_t, struct group **);
+struct group *getgrnam(const char *);
+int getgrnam_r(const char *, struct group *, char *, size_t, struct group **);
+void setgrent(void);
+int putgrent(const struct group *, FILE *);
+struct group *fgetgrent(FILE *);
+
+int setgroups(size_t size, const gid_t *list);
+int initgroups(const char *user, gid_t group);
+
+// Non standard extension
+int getgrouplist(const char *, gid_t, gid_t *, int *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _GRP_H
diff --git a/lib/mlibc/options/posix/include/langinfo.h b/lib/mlibc/options/posix/include/langinfo.h
new file mode 100644
index 0000000..5436a54
--- /dev/null
+++ b/lib/mlibc/options/posix/include/langinfo.h
@@ -0,0 +1,24 @@
+
+#ifndef _LANGINFO_H
+#define _LANGINFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/posix/locale_t.h>
+#include <bits/nl_item.h>
+
+#ifndef __MLIBC_ABI_ONLY
+
+char *nl_langinfo(nl_item);
+char *nl_langinfo_l(nl_item, locale_t);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LANGINFO_H
+
diff --git a/lib/mlibc/options/posix/include/libgen.h b/lib/mlibc/options/posix/include/libgen.h
new file mode 100644
index 0000000..fa53fc5
--- /dev/null
+++ b/lib/mlibc/options/posix/include/libgen.h
@@ -0,0 +1,28 @@
+
+#ifndef _LIBGEN_H
+#define _LIBGEN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(basename) && defined(_GNU_SOURCE)
+/* see: ./options/ansi/include/string.h, search for __mlibc_gnu_basename */
+# undef basename
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+char *basename(char *);
+#define basename basename
+char *dirname(char *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBGEN_H
+
+
diff --git a/lib/mlibc/options/posix/include/mlibc/lookup.hpp b/lib/mlibc/options/posix/include/mlibc/lookup.hpp
new file mode 100644
index 0000000..71f84e7
--- /dev/null
+++ b/lib/mlibc/options/posix/include/mlibc/lookup.hpp
@@ -0,0 +1,58 @@
+#ifndef _MLIBC_LOOKUP
+#define _MLIBC_LOOKUP
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <frg/string.hpp>
+#include <frg/vector.hpp>
+#include <frg/span.hpp>
+#include <frg/array.hpp>
+#include <mlibc/allocator.hpp>
+
+namespace mlibc {
+
+struct dns_addr_buf {
+ dns_addr_buf()
+ : name(getAllocator()) {}
+ frg::string<MemoryAllocator> name;
+ int family;
+ uint8_t addr[16];
+};
+
+struct lookup_result {
+ lookup_result()
+ : buf(getAllocator()), aliases(getAllocator()) {}
+ frg::vector<struct dns_addr_buf, MemoryAllocator> buf;
+ frg::vector<frg::string<MemoryAllocator>, MemoryAllocator> aliases;
+};
+
+struct dns_header {
+ uint16_t identification;
+ uint16_t flags;
+ uint16_t no_q;
+ uint16_t no_ans;
+ uint16_t no_auths;
+ uint16_t no_additional;
+};
+
+struct ai_buf {
+ struct addrinfo ai;
+ union sa {
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } sa;
+};
+
+int lookup_name_dns(struct lookup_result &buf, const char *name,
+ frg::string<MemoryAllocator> &canon_name);
+int lookup_addr_dns(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family);
+int lookup_name_hosts(struct lookup_result &buf, const char *name,
+ frg::string<MemoryAllocator> &canon_name);
+int lookup_addr_hosts(frg::span<char> name, frg::array<uint8_t, 16> &addr, int family);
+int lookup_name_null(struct lookup_result &buf, int flags, int family);
+int lookup_name_ip(struct lookup_result &buf, const char *name, int family);
+
+} // namespace mlibc
+
+#endif // _MLIBC_LOOKUP
diff --git a/lib/mlibc/options/posix/include/mlibc/posix-file-io.hpp b/lib/mlibc/options/posix/include/mlibc/posix-file-io.hpp
new file mode 100644
index 0000000..ac316ac
--- /dev/null
+++ b/lib/mlibc/options/posix/include/mlibc/posix-file-io.hpp
@@ -0,0 +1,102 @@
+#ifndef MLIBC_POSIX_FILE_IO_HPP
+#define MLIBC_POSIX_FILE_IO_HPP
+
+#include <frg/span.hpp>
+#include <frg/vector.hpp>
+#include <mlibc/file-io.hpp>
+#include <mlibc/allocator.hpp>
+
+namespace mlibc {
+
+struct mem_file : abstract_file {
+ mem_file(int flags, void (*do_dispose)(abstract_file *) = nullptr) : abstract_file{do_dispose}, _flags{flags} { };
+
+ int reopen(const char *path, const char *mode) override;
+protected:
+ int determine_type(stream_type *type) override;
+ int determine_bufmode(buffer_mode *mode) override;
+
+ virtual frg::span<char> _buffer() = 0;
+ virtual size_t _buffer_size() const = 0;
+
+ off_t _pos = 0;
+ int _flags = 0;
+ // maintains the size of buffer contents as required by POSIX
+ off_t _max_size = 0;
+};
+
+struct memstream_mem_file final : public mem_file {
+ memstream_mem_file(char **ptr, size_t *sizeloc, int flags, void (*do_dispose)(abstract_file *) = nullptr);
+
+ int close() override;
+
+ int io_read(char *buffer, size_t max_size, size_t *actual_size) override;
+ int io_write(const char *buffer, size_t max_size, size_t *actual_size) override;
+ int io_seek(off_t offset, int whence, off_t *new_offset) override;
+
+ frg::span<char> _buffer() override {
+ return {_buf.data(), _buffer_size()};
+ }
+
+ size_t _buffer_size() const override {
+ return _buf.size();
+ }
+
+private:
+ void _update_ptrs();
+
+ // Where to write back buffer and size on flush and close.
+ char **_bufloc;
+ size_t *_sizeloc;
+
+ frg::vector<char, MemoryAllocator> _buf = {getAllocator()};
+};
+
+struct fmemopen_mem_file final : public mem_file {
+ fmemopen_mem_file(void *in_buf, size_t size, int flags, void (*do_dispose)(abstract_file *) = nullptr);
+
+ int close() override;
+
+ int io_read(char *buffer, size_t max_size, size_t *actual_size) override;
+ int io_write(const char *buffer, size_t max_size, size_t *actual_size) override;
+ int io_seek(off_t offset, int whence, off_t *new_offset) override;
+
+ frg::span<char> _buffer() override {
+ return {reinterpret_cast<char *>(_inBuffer), _buffer_size()};
+ }
+
+ size_t _buffer_size() const override {
+ return _inBufferSize;
+ }
+
+private:
+ void *_inBuffer;
+ size_t _inBufferSize;
+
+ bool _needsDeallocation = false;
+};
+
+struct cookie_file : abstract_file {
+ cookie_file(void *cookie, int flags, cookie_io_functions_t funcs, void (*do_dispose)(abstract_file *) = nullptr)
+ : abstract_file{do_dispose}, _cookie{cookie}, _flags{flags}, _funcs{funcs} { }
+
+ int close() override;
+ int reopen(const char *path, const char *mode) override;
+protected:
+ int determine_type(stream_type *type) override;
+ int determine_bufmode(buffer_mode *mode) override;
+
+ int io_read(char *buffer, size_t max_size, size_t *actual_size) override;
+ int io_write(const char *buffer, size_t max_size, size_t *actual_size) override;
+ int io_seek(off_t offset, int whence, off_t *new_offset) override;
+
+private:
+ void *_cookie;
+
+ int _flags;
+ cookie_io_functions_t _funcs;
+};
+
+} // namespace mlibc
+
+#endif // MLIBC_POSIX_FILE_IO_HPP
diff --git a/lib/mlibc/options/posix/include/mlibc/posix-sysdeps.hpp b/lib/mlibc/options/posix/include/mlibc/posix-sysdeps.hpp
new file mode 100644
index 0000000..ba77b32
--- /dev/null
+++ b/lib/mlibc/options/posix/include/mlibc/posix-sysdeps.hpp
@@ -0,0 +1,240 @@
+#ifndef MLIBC_POSIX_SYSDEPS
+#define MLIBC_POSIX_SYSDEPS
+
+#include <stddef.h>
+
+#include <abi-bits/seek-whence.h>
+#include <abi-bits/vm-flags.h>
+#include <bits/off_t.h>
+#include <bits/ssize_t.h>
+#include <mlibc/fsfd_target.hpp>
+
+#include <fcntl.h>
+#include <time.h>
+#include <abi-bits/pid_t.h>
+#include <abi-bits/socklen_t.h>
+#include <bits/posix/stat.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <sys/select.h>
+#include <sys/sem.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <sched.h>
+#include <termios.h>
+#include <time.h>
+#include <ucontext.h>
+
+namespace [[gnu::visibility("hidden")]] mlibc {
+
+void sys_libc_log(const char *message);
+[[noreturn]] void sys_libc_panic();
+
+[[noreturn]] void sys_exit(int status);
+[[noreturn, gnu::weak]] void sys_thread_exit();
+int sys_clock_get(int clock, time_t *secs, long *nanos);
+
+int sys_open(const char *pathname, int flags, mode_t mode, int *fd);
+[[gnu::weak]] int sys_flock(int fd, int options);
+
+[[gnu::weak]] int sys_open_dir(const char *path, int *handle);
+[[gnu::weak]] int sys_read_entries(int handle, void *buffer, size_t max_size,
+ size_t *bytes_read);
+
+int sys_read(int fd, void *buf, size_t count, ssize_t *bytes_read);
+[[gnu::weak]] int sys_readv(int fd, const struct iovec *iovs, int iovc, ssize_t *bytes_read);
+
+int sys_write(int fd, const void *buf, size_t count, ssize_t *bytes_written);
+[[gnu::weak]] int sys_pread(int fd, void *buf, size_t n, off_t off, ssize_t *bytes_read);
+[[gnu::weak]] int sys_pwrite(int fd, const void *buf, size_t n, off_t off, ssize_t *bytes_read);
+
+int sys_seek(int fd, off_t offset, int whence, off_t *new_offset);
+int sys_close(int fd);
+
+[[gnu::weak]] int sys_access(const char *path, int mode);
+[[gnu::weak]] int sys_faccessat(int dirfd, const char *pathname, int mode, int flags);
+[[gnu::weak]] int sys_dup(int fd, int flags, int *newfd);
+[[gnu::weak]] int sys_dup2(int fd, int flags, int newfd);
+// In contrast to the isatty() library function, the sysdep function uses return value
+// zero (and not one) to indicate that the file is a terminal.
+[[gnu::weak]] int sys_isatty(int fd);
+[[gnu::weak]] int sys_stat(fsfd_target fsfdt, int fd, const char *path, int flags,
+ struct stat *statbuf);
+[[gnu::weak]] int sys_statvfs(const char *path, struct statvfs *out);
+[[gnu::weak]] int sys_fstatvfs(int fd, struct statvfs *out);
+[[gnu::weak]] int sys_readlink(const char *path, void *buffer, size_t max_size, ssize_t *length);
+[[gnu::weak]] int sys_rmdir(const char *path);
+[[gnu::weak]] int sys_ftruncate(int fd, size_t size);
+[[gnu::weak]] int sys_fallocate(int fd, off_t offset, size_t size);
+[[gnu::weak]] int sys_unlinkat(int fd, const char *path, int flags);
+[[gnu::weak]] int sys_openat(int dirfd, const char *path, int flags, mode_t mode, int *fd);
+[[gnu::weak]] int sys_socket(int family, int type, int protocol, int *fd);
+[[gnu::weak]] int sys_msg_send(int fd, const struct msghdr *hdr, int flags, ssize_t *length);
+[[gnu::weak]] ssize_t sys_sendto(int fd, const void *buffer, size_t size, int flags, const struct sockaddr *sock_addr, socklen_t addr_length, ssize_t *length);
+[[gnu::weak]] int sys_msg_recv(int fd, struct msghdr *hdr, int flags, ssize_t *length);
+[[gnu::weak]] ssize_t sys_recvfrom(int fd, void *buffer, size_t size, int flags, struct sockaddr *sock_addr, socklen_t *addr_length, ssize_t *length);
+[[gnu::weak]] int sys_listen(int fd, int backlog);
+[[gnu::weak]] gid_t sys_getgid();
+[[gnu::weak]] gid_t sys_getegid();
+[[gnu::weak]] uid_t sys_getuid();
+[[gnu::weak]] uid_t sys_geteuid();
+[[gnu::weak]] pid_t sys_getpid();
+[[gnu::weak]] pid_t sys_gettid();
+[[gnu::weak]] pid_t sys_getppid();
+[[gnu::weak]] int sys_getpgid(pid_t pid, pid_t *pgid);
+[[gnu::weak]] int sys_getsid(pid_t pid, pid_t *sid);
+[[gnu::weak]] int sys_setpgid(pid_t pid, pid_t pgid);
+[[gnu::weak]] int sys_setuid(uid_t uid);
+[[gnu::weak]] int sys_seteuid(uid_t euid);
+[[gnu::weak]] int sys_setgid(gid_t gid);
+[[gnu::weak]] int sys_setegid(gid_t egid);
+[[gnu::weak]] int sys_getgroups(size_t size, const gid_t *list, int *ret);
+[[gnu::weak]] void sys_yield();
+[[gnu::weak]] int sys_sleep(time_t *secs, long *nanos);
+[[gnu::weak]] int sys_fork(pid_t *child);
+[[gnu::weak]] int sys_execve(const char *path, char *const argv[], char *const envp[]);
+[[gnu::weak]] int sys_pselect(int num_fds, fd_set *read_set, fd_set *write_set,
+ fd_set *except_set, const struct timespec *timeout, const sigset_t *sigmask, int *num_events);
+[[gnu::weak]] int sys_getrusage(int scope, struct rusage *usage);
+[[gnu::weak]] int sys_getrlimit(int resource, struct rlimit *limit);
+[[gnu::weak]] int sys_setrlimit(int resource, const struct rlimit *limit);
+[[gnu::weak]] int sys_getpriority(int which, id_t who, int *value);
+[[gnu::weak]] int sys_setpriority(int which, id_t who, int prio);
+[[gnu::weak]] int sys_getschedparam(void *tcb, int *policy, struct sched_param *param);
+[[gnu::weak]] int sys_setschedparam(void *tcb, int policy, const struct sched_param *param);
+[[gnu::weak]] int sys_get_max_priority(int policy, int *out);
+[[gnu::weak]] int sys_get_min_priority(int policy, int *out);
+[[gnu::weak]] int sys_getcwd(char *buffer, size_t size);
+[[gnu::weak]] int sys_chdir(const char *path);
+[[gnu::weak]] int sys_fchdir(int fd);
+[[gnu::weak]] int sys_chroot(const char *path);
+[[gnu::weak]] int sys_mkdir(const char *path, mode_t mode);
+[[gnu::weak]] int sys_mkdirat(int dirfd, const char *path, mode_t mode);
+[[gnu::weak]] int sys_link(const char *old_path, const char *new_path);
+[[gnu::weak]] int sys_linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags);
+[[gnu::weak]] int sys_symlink(const char *target_path, const char *link_path);
+[[gnu::weak]] int sys_symlinkat(const char *target_path, int dirfd, const char *link_path);
+[[gnu::weak]] int sys_rename(const char *path, const char *new_path);
+[[gnu::weak]] int sys_renameat(int olddirfd, const char *old_path, int newdirfd, const char *new_path);
+[[gnu::weak]] int sys_fcntl(int fd, int request, va_list args, int *result);
+[[gnu::weak]] int sys_ttyname(int fd, char *buf, size_t size);
+[[gnu::weak]] int sys_fadvise(int fd, off_t offset, off_t length, int advice);
+[[gnu::weak]] void sys_sync();
+[[gnu::weak]] int sys_fsync(int fd);
+[[gnu::weak]] int sys_fdatasync(int fd);
+[[gnu::weak]] int sys_chmod(const char *pathname, mode_t mode);
+[[gnu::weak]] int sys_fchmod(int fd, mode_t mode);
+[[gnu::weak]] int sys_fchmodat(int fd, const char *pathname, mode_t mode, int flags);
+[[gnu::weak]] int sys_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);
+[[gnu::weak]] int sys_mlock(const void *addr, size_t length);
+[[gnu::weak]] int sys_munlock(const void *addr, size_t length);
+[[gnu::weak]] int sys_mlockall(int flags);
+[[gnu::weak]] int sys_mlock(const void *addr, size_t len);
+[[gnu::weak]] int sys_munlockall(void);
+[[gnu::weak]] int sys_mincore(void *addr, size_t length, unsigned char *vec);
+
+// mlibc assumes that anonymous memory returned by sys_vm_map() is zeroed by the kernel / whatever is behind the sysdeps
+int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window);
+
+[[gnu::weak]] int sys_vm_remap(void *pointer, size_t size, size_t new_size, void **window);
+[[gnu::weak]] int sys_vm_protect(void *pointer, size_t size, int prot);
+
+int sys_vm_unmap(void *pointer, size_t size);
+
+[[gnu::weak]] int sys_setsid(pid_t *sid);
+[[gnu::weak]] int sys_tcgetattr(int fd, struct termios *attr);
+[[gnu::weak]] int sys_tcsetattr(int, int, const struct termios *attr);
+[[gnu::weak]] int sys_tcflow(int, int);
+[[gnu::weak]] int sys_tcflush(int fd, int queue);
+[[gnu::weak]] int sys_tcdrain(int);
+[[gnu::weak]] int sys_pipe(int *fds, int flags);
+[[gnu::weak]] int sys_socketpair(int domain, int type_and_flags, int proto, int *fds);
+[[gnu::weak]] int sys_poll(struct pollfd *fds, nfds_t count, int timeout, int *num_events);
+[[gnu::weak]] int sys_ioctl(int fd, unsigned long request, void *arg, int *result);
+[[gnu::weak]] int sys_getsockopt(int fd, int layer, int number,
+ void *__restrict buffer, socklen_t *__restrict size);
+[[gnu::weak]] int sys_setsockopt(int fd, int layer, int number,
+ const void *buffer, socklen_t size);
+[[gnu::weak]] int sys_shutdown(int sockfd, int how);
+[[gnu::weak]] int sys_sigprocmask(int how, const sigset_t *__restrict set,
+ sigset_t *__restrict retrieve);
+[[gnu::weak]] int sys_sigaction(int, const struct sigaction *__restrict,
+ struct sigaction *__restrict);
+// NOTE: POSIX says that behavior of timeout = nullptr is unspecified. We treat this case
+// as an infinite timeout, making sigtimedwait(..., nullptr) equivalent to sigwaitinfo(...)
+[[gnu::weak]] int sys_sigtimedwait(const sigset_t *__restrict set, siginfo_t *__restrict info, const struct timespec *__restrict timeout, int *out_signal);
+[[gnu::weak]] int sys_kill(int, int);
+[[gnu::weak]] int sys_accept(int fd, int *newfd, struct sockaddr *addr_ptr, socklen_t *addr_length, int flags);
+[[gnu::weak]] int sys_bind(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length);
+[[gnu::weak]] int sys_connect(int fd, const struct sockaddr *addr_ptr, socklen_t addr_length);
+[[gnu::weak]] int sys_sockname(int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length,
+ socklen_t *actual_length);
+[[gnu::weak]] int sys_peername(int fd, struct sockaddr *addr_ptr, socklen_t max_addr_length,
+ socklen_t *actual_length);
+[[gnu::weak]] int sys_gethostname(char *buffer, size_t bufsize);
+[[gnu::weak]] int sys_sethostname(const char *buffer, size_t bufsize);
+[[gnu::weak]] int sys_mkfifoat(int dirfd, const char *path, int mode);
+[[gnu::weak]] int sys_getentropy(void *buffer, size_t length);
+[[gnu::weak]] int sys_mknodat(int dirfd, const char *path, int mode, int dev);
+[[gnu::weak]] int sys_umask(mode_t mode, mode_t *old);
+
+[[gnu::weak]] int sys_before_cancellable_syscall(ucontext_t *uctx);
+[[gnu::weak]] int sys_tgkill(int tgid, int tid, int sig);
+
+[[gnu::weak]] int sys_fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags);
+[[gnu::weak]] int sys_sigaltstack(const stack_t *ss, stack_t *oss);
+[[gnu::weak]] int sys_sigsuspend(const sigset_t *set);
+[[gnu::weak]] int sys_sigpending(sigset_t *set);
+[[gnu::weak]] int sys_setgroups(size_t size, const gid_t *list);
+[[gnu::weak]] int sys_memfd_create(const char *name, int flags, int *fd);
+[[gnu::weak]] int sys_madvise(void *addr, size_t length, int advice);
+[[gnu::weak]] int sys_msync(void *addr, size_t length, int flags);
+
+[[gnu::weak]] int sys_getitimer(int which, struct itimerval *curr_value);
+[[gnu::weak]] int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
+[[gnu::weak]] int sys_timer_create(clockid_t clk, struct sigevent *__restrict evp, timer_t *__restrict res);
+[[gnu::weak]] int sys_timer_settime(timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old);
+[[gnu::weak]] int sys_timer_delete(timer_t t);
+[[gnu::weak]] int sys_times(struct tms *tms, clock_t *out);
+[[gnu::weak]] int sys_uname(struct utsname *buf);
+[[gnu::weak]] int sys_pause();
+
+[[gnu::weak]] int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid);
+[[gnu::weak]] int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+[[gnu::weak]] int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
+[[gnu::weak]] int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
+[[gnu::weak]] int sys_setreuid(uid_t ruid, uid_t euid);
+[[gnu::weak]] int sys_setregid(gid_t rgid, gid_t egid);
+
+[[gnu::weak]] int sys_poll(struct pollfd *fds, nfds_t count, int timeout, int *num_events);
+
+[[gnu::weak]] int sys_if_indextoname(unsigned int index, char *name);
+[[gnu::weak]] int sys_if_nametoindex(const char *name, unsigned int *ret);
+
+[[gnu::weak]] int sys_ptsname(int fd, char *buffer, size_t length);
+[[gnu::weak]] int sys_unlockpt(int fd);
+
+[[gnu::weak]] int sys_thread_setname(void *tcb, const char *name);
+[[gnu::weak]] int sys_thread_getname(void *tcb, char *name, size_t size);
+
+[[gnu::weak]] int sys_sysconf(int num, long *ret);
+
+[[gnu::weak]] int sys_semget(key_t key, int n, int fl, int *id);
+[[gnu::weak]] int sys_semctl(int semid, int semnum, int cmd, void *semun, int *ret);
+
+[[gnu::weak]] int sys_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
+[[gnu::weak]] int sys_getthreadaffinity(pid_t tid, size_t cpusetsize, cpu_set_t *mask);
+
+[[gnu::weak]] int sys_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);
+[[gnu::weak]] int sys_setthreadaffinity(pid_t tid, size_t cpusetsize, const cpu_set_t *mask);
+
+[[gnu::weak]] int sys_waitid(idtype_t idtype, id_t id, siginfo_t *info, int options);
+
+} //namespace mlibc
+
+#endif // MLIBC_POSIX_SYSDEPS
diff --git a/lib/mlibc/options/posix/include/mlibc/resolv_conf.hpp b/lib/mlibc/options/posix/include/mlibc/resolv_conf.hpp
new file mode 100644
index 0000000..2a349c7
--- /dev/null
+++ b/lib/mlibc/options/posix/include/mlibc/resolv_conf.hpp
@@ -0,0 +1,21 @@
+#ifndef _MLIBC_RESOLV_CONF
+#define _MLIBC_RESOLV_CONF
+
+#include <frg/string.hpp>
+#include <frg/optional.hpp>
+#include <mlibc/allocator.hpp>
+
+namespace mlibc {
+
+struct nameserver_data {
+ nameserver_data()
+ : name(getAllocator()) {}
+ frg::string<MemoryAllocator> name;
+ // for in the future we can also store options here
+};
+
+frg::optional<struct nameserver_data> get_nameserver();
+
+} // namespace mlibc
+
+#endif // _MLIBC_RESOLV_CONF
diff --git a/lib/mlibc/options/posix/include/mlibc/services.hpp b/lib/mlibc/options/posix/include/mlibc/services.hpp
new file mode 100644
index 0000000..10dec47
--- /dev/null
+++ b/lib/mlibc/options/posix/include/mlibc/services.hpp
@@ -0,0 +1,33 @@
+#ifndef _MLIBC_SERVICES
+#define _MLIBC_SERVICES
+
+#include <frg/small_vector.hpp>
+#include <frg/vector.hpp>
+#include <frg/string.hpp>
+#include <mlibc/allocator.hpp>
+
+namespace mlibc {
+
+// Only two services for tcp and udp
+#define SERV_MAX 2
+
+struct service_buf {
+ service_buf()
+ : name(getAllocator()), aliases(getAllocator())
+ { }
+ int port, protocol, socktype;
+ frg::string<MemoryAllocator> name;
+ frg::vector<frg::string<MemoryAllocator>, MemoryAllocator> aliases;
+};
+
+using service_result = frg::small_vector<service_buf, SERV_MAX, MemoryAllocator>;
+
+int lookup_serv_by_name(service_result &buf, const char *name, int proto,
+ int socktype, int flags);
+
+int lookup_serv_by_port(service_result &buf, int proto, int port);
+
+
+} // namespace mlibc
+
+#endif // _MLIBC_SERVICES
diff --git a/lib/mlibc/options/posix/include/mqueue.h b/lib/mlibc/options/posix/include/mqueue.h
new file mode 100644
index 0000000..34ac990
--- /dev/null
+++ b/lib/mlibc/options/posix/include/mqueue.h
@@ -0,0 +1,26 @@
+#ifndef _MQUEUE_H
+#define _MQUEUE_H
+
+#include <abi-bits/mqueue.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int mqd_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
+int mq_setattr(mqd_t mqdes, const struct mq_attr *__restrict__ newattr, struct mq_attr *__restrict__ oldattr);
+int mq_unlink(const char *name);
+mqd_t mq_open(const char *name, int flags, ...);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MQUEUE_H */
+
diff --git a/lib/mlibc/options/posix/include/net/if.h b/lib/mlibc/options/posix/include/net/if.h
new file mode 100644
index 0000000..10016fd
--- /dev/null
+++ b/lib/mlibc/options/posix/include/net/if.h
@@ -0,0 +1,118 @@
+
+#ifndef _NET_IF_H
+#define _NET_IF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+#define IF_NAMESIZE 16
+#define IFNAMSIZ IF_NAMESIZE
+#define ALTIFNAMSIZ 128
+#define IFALIASZ 256
+
+struct if_nameindex {
+ unsigned int if_index;
+ char *if_name;
+};
+
+struct ifmap {
+ unsigned long mem_start;
+ unsigned long mem_end;
+ unsigned short base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+};
+
+struct ifreq {
+ union {
+ char ifrn_name[IFNAMSIZ];
+ } ifr_ifrn;
+
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short int ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ struct ifmap ifru_map;
+ char ifru_slave[IFNAMSIZ];
+ char ifru_newname[IFNAMSIZ];
+ char *ifru_data;
+ } ifr_ifru;
+};
+
+#define ifr_name ifr_ifrn.ifrn_name
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr
+#define ifr_addr ifr_ifru.ifru_addr
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr
+#define ifr_netmask ifr_ifru.ifru_netmask
+#define ifr_flags ifr_ifru.ifru_flags
+#define ifr_metric ifr_ifru.ifru_ivalue
+#define ifr_mtu ifr_ifru.ifru_mtu
+#define ifr_map ifr_ifru.ifru_map
+#define ifr_slave ifr_ifru.ifru_slave
+#define ifr_data ifr_ifru.ifru_data
+#define ifr_ifindex ifr_ifru.ifru_ivalue
+#define ifr_bandwidth ifr_ifru.ifru_ivalue
+#define ifr_qlen ifr_ifru.ifru_ivalue
+#define ifr_newname ifr_ifru.ifru_newname
+
+struct ifconf {
+ int ifc_len;
+ union {
+ char *ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+};
+
+#define ifc_buf ifc_ifcu.ifcu_buf
+#define ifc_req ifc_ifcu.ifcu_req
+
+#ifndef __MLIBC_ABI_ONLY
+
+void if_freenameindex(struct if_nameindex *);
+char *if_indextoname(unsigned int, char *);
+struct if_nameindex *if_nameindex(void);
+unsigned int if_nametoindex(const char *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#define IFHWADDRLEN 6
+
+#define IFF_UP 0x1
+#define IFF_BROADCAST 0x2
+#define IFF_DEBUG 0x4
+#define IFF_LOOPBACK 0x8
+#define IFF_POINTOPOINT 0x10
+#define IFF_NOTRAILERS 0x20
+#define IFF_RUNNING 0x40
+#define IFF_NOARP 0x80
+#define IFF_PROMISC 0x100
+#define IFF_ALLMULTI 0x200
+#define IFF_MASTER 0x400
+#define IFF_SLAVE 0x800
+#define IFF_MULTICAST 0x1000
+#define IFF_PORTSEL 0x2000
+#define IFF_AUTOMEDIA 0x4000
+#define IFF_DYNAMIC 0x8000
+#define IFF_LOWER_UP 0x10000
+#define IFF_DORMANT 0x20000
+#define IFF_ECHO 0x40000
+#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST| \
+ IFF_ECHO|IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NET_IF_H
+
+
diff --git a/lib/mlibc/options/posix/include/net/if_arp.h b/lib/mlibc/options/posix/include/net/if_arp.h
new file mode 100644
index 0000000..de8a0c2
--- /dev/null
+++ b/lib/mlibc/options/posix/include/net/if_arp.h
@@ -0,0 +1,103 @@
+#ifndef _NET_IF_ARP_H
+#define _NET_IF_ARP_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+
+#define ARPOP_REQUEST 1
+#define ARPOP_REPLY 2
+#define ARPOP_RREQUEST 3
+#define ARPOP_RREPLY 4
+#define ARPOP_InREQUEST 8
+#define ARPOP_InREPLY 9
+#define ARPOP_NAK 10
+
+#define ARPHRD_NETROM 0
+#define ARPHRD_ETHER 1
+#define ARPHRD_EETHER 2
+#define ARPHRD_AX25 3
+#define ARPHRD_PRONET 4
+#define ARPHRD_CHAOS 5
+#define ARPHRD_IEEE802 6
+#define ARPHRD_ARCNET 7
+#define ARPHRD_APPLETLK 8
+#define ARPHRD_DLCI 15
+#define ARPHRD_ATM 19
+#define ARPHRD_METRICOM 23
+#define ARPHRD_IEEE1394 24
+#define ARPHRD_EUI64 27
+#define ARPHRD_INFINIBAND 32
+#define ARPHRD_SLIP 256
+#define ARPHRD_CSLIP 257
+#define ARPHRD_SLIP6 258
+#define ARPHRD_CSLIP6 259
+#define ARPHRD_RSRVD 260
+#define ARPHRD_ADAPT 264
+#define ARPHRD_ROSE 270
+#define ARPHRD_X25 271
+#define ARPHRD_HWX25 272
+#define ARPHRD_CAN 280
+#define ARPHRD_PPP 512
+#define ARPHRD_CISCO 513
+#define ARPHRD_HDLC ARPHRD_CISCO
+#define ARPHRD_LAPB 516
+#define ARPHRD_DDCMP 517
+#define ARPHRD_RAWHDLC 518
+#define ARPHRD_RAWIP 519
+
+#define ARPHRD_TUNNEL 768
+#define ARPHRD_TUNNEL6 769
+#define ARPHRD_FRAD 770
+#define ARPHRD_SKIP 771
+#define ARPHRD_LOOPBACK 772
+#define ARPHRD_LOCALTLK 773
+#define ARPHRD_FDDI 774
+#define ARPHRD_BIF 775
+#define ARPHRD_SIT 776
+#define ARPHRD_IPDDP 777
+#define ARPHRD_IPGRE 778
+#define ARPHRD_PIMREG 779
+#define ARPHRD_HIPPI 780
+#define ARPHRD_ASH 781
+#define ARPHRD_ECONET 782
+#define ARPHRD_IRDA 783
+#define ARPHRD_FCPP 784
+#define ARPHRD_FCAL 785
+#define ARPHRD_FCPL 786
+#define ARPHRD_FCFABRIC 787
+#define ARPHRD_IEEE802_TR 800
+#define ARPHRD_IEEE80211 801
+#define ARPHRD_IEEE80211_PRISM 802
+#define ARPHRD_IEEE80211_RADIOTAP 803
+#define ARPHRD_IEEE802154 804
+#define ARPHRD_IEEE802154_MONITOR 805
+#define ARPHRD_PHONET 820
+#define ARPHRD_PHONET_PIPE 821
+#define ARPHRD_CAIF 822
+#define ARPHRD_IP6GRE 823
+#define ARPHRD_NETLINK 824
+#define ARPHRD_6LOWPAN 825
+#define ARPHRD_VSOCKMON 826
+
+#define ARPHRD_VOID 0xFFFF
+#define ARPHRD_NONE 0xFFFE
+
+struct arphdr {
+ uint16_t ar_hrd;
+ uint16_t ar_pro;
+ uint8_t ar_hln;
+ uint8_t ar_pln;
+ uint16_t ar_op;
+};
+
+struct arpreq {
+ struct sockaddr arp_pa;
+ struct sockaddr arp_ha;
+ int arp_flags;
+ struct sockaddr arp_netmask;
+ char arp_dev[16];
+};
+
+#endif // _NET_IF_ARP_H
+
diff --git a/lib/mlibc/options/posix/include/netdb.h b/lib/mlibc/options/posix/include/netdb.h
new file mode 100644
index 0000000..368c74f
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netdb.h
@@ -0,0 +1,148 @@
+#ifndef _NETDB_H
+#define _NETDB_H
+
+#include <stdint.h>
+#include <bits/size_t.h>
+#include <bits/posix/in_port_t.h>
+#include <bits/posix/in_addr_t.h>
+#include <abi-bits/socklen_t.h>
+
+#define AI_PASSIVE 0x01
+#define AI_CANONNAME 0x02
+#define AI_NUMERICHOST 0x04
+#define AI_V4MAPPED 0x08
+#define AI_ALL 0x10
+#define AI_ADDRCONFIG 0x20
+#define AI_NUMERICSERV 0x40
+
+#define NI_NOFQDN 0x01
+#define NI_NUMERICHOST 0x02
+#define NI_NAMEREQD 0x04
+#define NI_NUMERICSCOPE 0x08
+#define NI_DGRAM 0x10
+
+#define NI_NUMERICSERV 2
+#define NI_MAXSERV 32
+#define NI_IDN 32
+
+#define NI_MAXHOST 1025
+
+#define EAI_AGAIN 1
+#define EAI_BADFLAGS 2
+#define EAI_FAIL 3
+#define EAI_FAMILY 4
+#define EAI_MEMORY 5
+#define EAI_NONAME 6
+#define EAI_SERVICE 7
+#define EAI_SOCKTYPE 8
+#define EAI_SYSTEM 9
+#define EAI_OVERFLOW 10
+#define EAI_NODATA 11
+#define EAI_ADDRFAMILY 12
+
+#define HOST_NOT_FOUND 1
+#define TRY_AGAIN 2
+#define NO_RECOVERY 3
+#define NO_DATA 4
+#define NO_ADDRESS NO_DATA
+
+#define IPPORT_RESERVED 1024
+
+#define _PATH_SERVICES "/etc/services"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int *__h_errno_location(void);
+#define h_errno (*__h_errno_location())
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+struct hostent {
+ char *h_name;
+ char **h_aliases;
+ int h_addrtype;
+ int h_length;
+ char **h_addr_list;
+};
+
+#define h_addr h_addr_list[0] // Required by some programs
+
+struct netent {
+ char *n_name;
+ char **n_aliases;
+ int n_addrtype;
+ uint32_t n_net;
+};
+
+struct protoent {
+ char *p_name;
+ char **p_aliases;
+ int p_proto;
+};
+
+struct servent {
+ char *s_name;
+ char **s_aliases;
+ int s_port;
+ char *s_proto;
+};
+
+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;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+void endhostent(void);
+void endnetent(void);
+void endprotoent(void);
+void endservent(void);
+void freeaddrinfo(struct addrinfo *);
+const char *gai_strerror(int);
+int getaddrinfo(const char *__restrict, const char *__restrict,
+ const struct addrinfo *__restrict, struct addrinfo **__restrict);
+struct hostent *gethostent(void);
+struct hostent *gethostbyname(const char *);
+struct hostent *gethostbyname2(const char *, int);
+struct hostent *gethostbyaddr(const void *, socklen_t, int);
+int gethostbyaddr_r(const void *__restrict, socklen_t, int, struct hostent *__restrict,
+ char *__restrict, size_t, struct hostent **__restrict, int *__restrict);
+int gethostbyname_r(const char *__restrict, struct hostent *__restrict, char *__restrict, size_t,
+ struct hostent **__restrict, int *__restrict);
+int getnameinfo(const struct sockaddr *__restrict, socklen_t,
+ char *__restrict, socklen_t, char *__restrict, socklen_t, int);
+struct netent *getnetbyaddr(uint32_t, int);
+struct netent *getnetbyname(const char *);
+struct netent *getnetent(void);
+struct protoent *getprotobyname(const char *);
+struct protoent *getprotobynumber(int);
+struct protoent *getprotoent(void);
+struct servent *getservbyname(const char *, const char *);
+struct servent *getservbyport(int, const char *);
+struct servent *getservent(void);
+void sethostent(int);
+void setnetent(int);
+void setprotoent(int);
+void setservent(int);
+
+// Deprecated GNU extension
+const char *hstrerror(int err);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETDB_H
diff --git a/lib/mlibc/options/posix/include/netinet/icmp6.h b/lib/mlibc/options/posix/include/netinet/icmp6.h
new file mode 100644
index 0000000..7dfe237
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/icmp6.h
@@ -0,0 +1,139 @@
+#ifndef _NETINET_ICMP6_H
+#define _NETINET_ICMP6_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <abi-bits/in.h>
+#include <mlibc-config.h>
+
+#if __MLIBC_GLIBC_OPTION
+#include <bits/glibc/glibc_icmp6.h>
+#endif // __MLIBC_GLIBC_OPTION
+
+#define ICMP6_FILTER 1
+
+#define ICMP6_FILTER_BLOCK 1
+#define ICMP6_FILTER_PASS 2
+#define ICMP6_FILTER_BLOCKOTHERS 3
+#define ICMP6_FILTER_PASSONLY 4
+#define ICMP6_ECHO_REQUEST 128
+
+struct icmp6_filter {
+ uint32_t icmp6_filt[8];
+};
+
+struct icmp6_hdr {
+ uint8_t icmp6_type;
+ uint8_t icmp6_code;
+ uint16_t icmp6_cksum;
+ union {
+ uint32_t icmp6_un_data32[1];
+ uint16_t icmp6_un_data16[2];
+ uint8_t icmp6_un_data8[4];
+ } icmp6_dataun;
+};
+
+#define icmp6_data32 icmp6_dataun.icmp6_un_data32
+#define icmp6_data16 icmp6_dataun.icmp6_un_data16
+#define icmp6_data8 icmp6_dataun.icmp6_un_data8
+
+#define icmp6_pptr icmp6_data32[0]
+#define icmp6_mtu icmp6_data32[0]
+#define icmp6_id icmp6_data16[0]
+#define icmp6_seq icmp6_data16[1]
+#define icmp6_maxdelay icmp6_data16[0]
+
+#define ICMP6_FILTER_WILLPASS(type, filterp) \
+ ((((filterp)->icmp6_filt[(type) >> 5]) & (1U << ((type) & 31))) == 0)
+
+#define ICMP6_FILTER_WILLBLOCK(type, filterp) \
+ ((((filterp)->icmp6_filt[(type) >> 5]) & (1U << ((type) & 31))) != 0)
+
+#define ICMP6_FILTER_SETPASS(type, filterp) \
+ ((((filterp)->icmp6_filt[(type) >> 5]) &= ~(1U << ((type) & 31))))
+
+#define ICMP6_FILTER_SETBLOCK(type, filterp) \
+ ((((filterp)->icmp6_filt[(type) >> 5]) |= (1U << ((type) & 31))))
+
+#define ICMP6_FILTER_SETPASSALL(filterp) \
+ memset (filterp, 0, sizeof (struct icmp6_filter));
+
+#define ICMP6_FILTER_SETBLOCKALL(filterp) \
+ memset (filterp, 0xFF, sizeof (struct icmp6_filter));
+
+#define ND_ROUTER_SOLICIT 133
+#define ND_ROUTER_ADVERT 134
+#define ND_NEIGHBOR_SOLICIT 135
+#define ND_NEIGHBOR_ADVERT 136
+#define ND_REDIRECT 137
+
+struct nd_router_solicit {
+ struct icmp6_hdr nd_rs_hdr;
+};
+
+#define nd_rs_type nd_rs_hdr.icmp6_type
+#define nd_rs_code nd_rs_hdr.icmp6_code
+#define nd_rs_cksum nd_rs_hdr.icmp6_cksum
+#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0]
+
+struct nd_router_advert {
+ struct icmp6_hdr nd_ra_hdr;
+ uint32_t nd_ra_reachable;
+ uint32_t nd_ra_retransmit;
+};
+
+struct nd_opt_hdr {
+ uint8_t nd_opt_type;
+ uint8_t nd_opt_len;
+};
+
+#define ND_OPT_SOURCE_LINKADDR 1
+#define ND_OPT_TARGET_LINKADDR 2
+#define ND_OPT_PREFIX_INFORMATION 3
+#define ND_OPT_REDIRECTED_HEADER 4
+#define ND_OPT_MTU 5
+#define ND_OPT_RTR_ADV_INTERVAL 7
+#define ND_OPT_HOME_AGENT_INFO 8
+
+struct nd_opt_prefix_info {
+ uint8_t nd_opt_pi_type;
+ uint8_t nd_opt_pi_len;
+ uint8_t nd_opt_pi_prefix_len;
+ uint8_t nd_opt_pi_flags_reserved;
+ uint32_t nd_opt_pi_valid_time;
+ uint32_t nd_opt_pi_preferred_time;
+ uint32_t nd_opt_pi_reserved2;
+ struct in6_addr nd_opt_pi_prefix;
+};
+
+#define ND_OPT_PI_FLAG_RADDR 0x20
+#define ND_OPT_PI_FLAG_AUTO 0x40
+#define ND_OPT_PI_FLAG_ONLINK 0x80
+
+#define nd_ra_type nd_ra_hdr.icmp6_type
+#define nd_ra_code nd_ra_hdr.icmp6_code
+#define nd_ra_cksum nd_ra_hdr.icmp6_cksum
+#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0]
+#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1]
+#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
+
+#define ND_RA_FLAG_HOME_AGENT 0x20
+#define ND_RA_FLAG_OTHER 0x40
+#define ND_RA_FLAG_MANAGED 0x80
+
+struct nd_opt_mtu {
+ uint8_t nd_opt_mtu_type;
+ uint8_t nd_opt_mtu_len;
+ uint16_t nd_opt_mtu_reserved;
+ uint32_t nd_opt_mtu_mtu;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_ICMP6_H
+
diff --git a/lib/mlibc/options/posix/include/netinet/if_ether.h b/lib/mlibc/options/posix/include/netinet/if_ether.h
new file mode 100644
index 0000000..c4ce173
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/if_ether.h
@@ -0,0 +1,105 @@
+#ifndef _NETINET_IF_ETHER_H
+#define _NETINET_IF_ETHER_H
+
+#include <net/if_arp.h>
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+#define ETH_ZLEN 60
+#define ETH_FRAME_LEN 1514
+#define ETH_FCS_LEN 4
+
+#define ETH_P_LOOP 0x0060
+#define ETH_P_PUP 0x0200
+#define ETH_P_PUPAT 0x0201
+#define ETH_P_IP 0x0800
+#define ETH_P_X25 0x0805
+#define ETH_P_ARP 0x0806
+#define ETH_P_BPQ 0x08FF
+#define ETH_P_IEEEPUP 0x0a00
+#define ETH_P_IEEEPUPAT 0x0a01
+#define ETH_P_BATMAN 0x4305
+#define ETH_P_DEC 0x6000
+#define ETH_P_DNA_DL 0x6001
+#define ETH_P_DNA_RC 0x6002
+#define ETH_P_DNA_RT 0x6003
+#define ETH_P_LAT 0x6004
+#define ETH_P_DIAG 0x6005
+#define ETH_P_CUST 0x6006
+#define ETH_P_SCA 0x6007
+#define ETH_P_TEB 0x6558
+#define ETH_P_RARP 0x8035
+#define ETH_P_ATALK 0x809B
+#define ETH_P_AARP 0x80F3
+#define ETH_P_8021Q 0x8100
+#define ETH_P_IPX 0x8137
+#define ETH_P_IPV6 0x86DD
+#define ETH_P_PAUSE 0x8808
+#define ETH_P_SLOW 0x8809
+#define ETH_P_WCCP 0x883E
+#define ETH_P_MPLS_UC 0x8847
+#define ETH_P_MPLS_MC 0x8848
+#define ETH_P_ATMMPOA 0x884c
+#define ETH_P_PPP_DISC 0x8863
+#define ETH_P_PPP_SES 0x8864
+#define ETH_P_LINK_CTL 0x886c
+#define ETH_P_ATMFATE 0x8884
+#define ETH_P_PAE 0x888E
+#define ETH_P_AOE 0x88A2
+#define ETH_P_8021AD 0x88A8
+#define ETH_P_802_EX1 0x88B5
+#define ETH_P_TIPC 0x88CA
+#define ETH_P_8021AH 0x88E7
+#define ETH_P_MVRP 0x88F5
+#define ETH_P_1588 0x88F7
+#define ETH_P_PRP 0x88FB
+#define ETH_P_FCOE 0x8906
+#define ETH_P_TDLS 0x890D
+#define ETH_P_FIP 0x8914
+#define ETH_P_80221 0x8917
+#define ETH_P_LOOPBACK 0x9000
+#define ETH_P_QINQ1 0x9100
+#define ETH_P_QINQ2 0x9200
+#define ETH_P_QINQ3 0x9300
+#define ETH_P_EDSA 0xDADA
+#define ETH_P_AF_IUCV 0xFBFB
+
+#define ETH_P_802_3_MIN 0x0600
+
+#define ETH_P_802_3 0x0001
+#define ETH_P_AX25 0x0002
+#define ETH_P_ALL 0x0003
+#define ETH_P_802_2 0x0004
+#define ETH_P_SNAP 0x0005
+#define ETH_P_DDCMP 0x0006
+#define ETH_P_WAN_PPP 0x0007
+#define ETH_P_PPP_MP 0x0008
+#define ETH_P_LOCALTALK 0x0009
+#define ETH_P_CAN 0x000C
+#define ETH_P_CANFD 0x000D
+#define ETH_P_PPPTALK 0x0010
+#define ETH_P_TR_802_2 0x0011
+#define ETH_P_MOBITEX 0x0015
+#define ETH_P_CONTROL 0x0016
+#define ETH_P_IRDA 0x0017
+#define ETH_P_ECONET 0x0018
+#define ETH_P_HDLC 0x0019
+#define ETH_P_ARCNET 0x001A
+#define ETH_P_DSA 0x001B
+#define ETH_P_TRAILER 0x001C
+#define ETH_P_PHONET 0x00F5
+#define ETH_P_IEEE802154 0x00F6
+#define ETH_P_CAIF 0x00F7
+
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+struct ether_arp {
+ struct arphdr ea_hdr;
+ uint8_t arp_sha[ETH_ALEN];
+ uint8_t arp_spa[4];
+ uint8_t arp_tha[ETH_ALEN];
+ uint8_t arp_tpa[4];
+};
+
+#endif //_NETINET_IF_ETHER_H
diff --git a/lib/mlibc/options/posix/include/netinet/in.h b/lib/mlibc/options/posix/include/netinet/in.h
new file mode 100644
index 0000000..9a42c47
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/in.h
@@ -0,0 +1,118 @@
+
+#ifndef _NETINET_IN_H
+#define _NETINET_IN_H
+
+#include <stdint.h>
+#include <endian.h>
+#include <sys/socket.h> // struct sockaddr
+#include <abi-bits/socket.h>
+#include <abi-bits/in.h>
+#include <arpa/inet.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+extern const struct in6_addr in6addr_any;
+extern const struct in6_addr in6addr_loopback;
+
+uint32_t htonl(uint32_t);
+uint16_t htons(uint16_t);
+uint32_t ntohl(uint32_t);
+uint16_t ntohs(uint16_t);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#define IN6_IS_ADDR_UNSPECIFIED(a) ({ \
+ uint32_t *_a = (uint32_t *)(((struct in6_addr *) a)->s6_addr); \
+ !_a[0] && \
+ !_a[1] && \
+ !_a[2] && \
+ !_a[3]; \
+})
+#define IN6_IS_ADDR_LOOPBACK(a) ({ \
+ uint32_t *_a = (uint32_t *)(((struct in6_addr *) a)->s6_addr); \
+ !_a[0] && \
+ !_a[1] && \
+ !_a[2] && \
+ _a[3] == htonl(0x0001); \
+})
+#define IN6_IS_ADDR_MULTICAST(a) (((const uint8_t *) (a))[0] == 0xff)
+#define IN6_IS_ADDR_LINKLOCAL(a) ({ \
+ uint32_t *_a = (uint32_t *)(((struct in6_addr *) a)->s6_addr); \
+ _a[0] & htonl(0xffc00000) == htonl(0xfe800000); \
+})
+#define IN6_IS_ADDR_SITELOCAL(a) ({ \
+ uint32_t *_a = (uint32_t *)(((struct in6_addr *) a)->s6_addr); \
+ _a[0] & htonl(0xffc00000) == htonl(0xfec00000); \
+})
+#define IN6_IS_ADDR_V4MAPPED(a) ({ \
+ uint32_t *_a = (uint32_t *)(((struct in6_addr *) a)->s6_addr); \
+ !_a[0] && \
+ !_a[1] && \
+ _a[2] == htonl(0xffff); \
+})
+#define __ARE_4_BYTE_EQUAL(a, b) \
+ ((a)[0] == (b)[0] && (a)[1] == (b)[1] && (a)[2] == (b)[2] && \
+ (a)[3] == (b)[3] && (a)[4] == (b)[4])
+#define IN6_ARE_ADDR_EQUAL(a, b) \
+ __ARE_4_BYTE_EQUAL((const uint32_t *)(a), (const uint32_t *)(b))
+
+#define IN6_IS_ADDR_V4COMPAT(a) ({ \
+ uint32_t *_a = (uint32_t *)(((struct in6_addr *) a)->s6_addr); \
+ uint8_t *_a8 = (uint8_t *)(((struct in6_addr *) a)->s6_addr); \
+ !_a[0] && !_a[1] && !_a[2] && (_a8[15] > 1); \
+})
+#define IN6_IS_ADDR_MC_NODELOCAL(a) ({ \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ ((((const uint8_t *)(a))[1] & 0xf) == 0x1)); \
+})
+#define IN6_IS_ADDR_MC_LINKLOCAL(a) ({ \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ ((((const uint8_t *)(a))[1] & 0xf) == 0x2)); \
+})
+#define IN6_IS_ADDR_MC_SITELOCAL(a) ({ \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ ((((const uint8_t *)(a))[1] & 0xf) == 0x5)); \
+})
+#define IN6_IS_ADDR_MC_ORGLOCAL(a) ({ \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ ((((const uint8_t *)(a))[1] & 0xf) == 0x8)); \
+})
+#define IN6_IS_ADDR_MC_GLOBAL(a) ({ \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ ((((const uint8_t *)(a))[1] & 0xf) == 0xe)); \
+})
+
+#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET)
+#define IN_CLASSA_MAX 128
+#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET)
+#define IN_CLASSB_MAX 65536
+#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET)
+#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xe0000000)
+#define IN_MULTICAST(a) IN_CLASSD(a)
+#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000)
+#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000)
+
+#define IN_LOOPBACKNET 127
+
+#define MCAST_EXCLUDE 0
+#define MCAST_INCLUDE 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_IN_H
+
diff --git a/lib/mlibc/options/posix/include/netinet/ip.h b/lib/mlibc/options/posix/include/netinet/ip.h
new file mode 100644
index 0000000..161aa18
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/ip.h
@@ -0,0 +1,75 @@
+
+#ifndef _NETINET_IP_H
+#define _NETINET_IP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#define IPTOS_TOS_MASK 0x1E
+#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IPTOS_LOWCOST 0x02
+#define IPTOS_MINCOST IPTOS_LOWCOST
+#define IPTOS_CLASS_CS4 0x80
+#define IPTOS_CLASS_CS6 0xC0
+
+#define IPDEFTTL 64
+
+struct ip {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ unsigned int ip_hl:4;
+ unsigned int ip_v:4;
+#endif
+#if __BYTE_ORDER == __BIG_ENDIAN
+ unsigned int ip_v:4;
+ unsigned int ip_hl:4;
+#endif
+ uint8_t ip_tos;
+ unsigned short ip_len;
+ unsigned short ip_id;
+ unsigned short ip_off;
+#define IP_RF 0x8000
+#define IP_DF 0x4000
+#define IP_MF 0x2000
+#define IP_OFFMASK 0x1fff
+ uint8_t ip_ttl;
+ uint8_t ip_p;
+ unsigned short ip_sum;
+ struct in_addr ip_src, ip_dst;
+};
+
+#define IPVERSION 4
+
+struct iphdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ unsigned int ihl:4;
+ unsigned int version:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ unsigned int version:4;
+ unsigned int ihl:4;
+#else
+# error "Please fix <endian.h>"
+#endif
+ uint8_t tos;
+ uint16_t tot_len;
+ uint16_t id;
+ uint16_t frag_off;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t check;
+ uint32_t saddr;
+ uint32_t daddr;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_IP_H
+
diff --git a/lib/mlibc/options/posix/include/netinet/ip6.h b/lib/mlibc/options/posix/include/netinet/ip6.h
new file mode 100644
index 0000000..88f0cb6
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/ip6.h
@@ -0,0 +1,28 @@
+#ifndef _NETINET_IP6_H
+#define _NETINET_IP6_H
+
+#include <netinet/in.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ip6_hdr {
+ union {
+ struct ip6_hdrctl {
+ uint32_t ip6_un1_flow;
+ uint16_t ip6_un1_plen;
+ uint8_t ip6_un1_nxt;
+ uint8_t ip6_un1_hlim;
+ } ip6_un1;
+ uint8_t ip6_un2_vfc;
+ } ip6_ctlun;
+ struct in6_addr ip6_src;
+ struct in6_addr ip6_dst;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_IP6_H
diff --git a/lib/mlibc/options/posix/include/netinet/ip_icmp.h b/lib/mlibc/options/posix/include/netinet/ip_icmp.h
new file mode 100644
index 0000000..56615e4
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/ip_icmp.h
@@ -0,0 +1,84 @@
+#ifndef _NETINET_ICMP_H
+#define _NETINET_ICMP_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#define ICMP_ECHOREPLY 0
+#define ICMP_ECHO 8
+#define ICMP_ADVLENMIN (8 + sizeof(struct ip) + 8)
+
+struct icmp_ra_addr {
+ uint32_t ira_addr;
+ uint32_t ira_preference;
+};
+
+struct icmp {
+ uint8_t icmp_type;
+ uint8_t icmp_code;
+ uint16_t icmp_cksum;
+ union {
+ unsigned char ih_pptr;
+ struct in_addr ih_gwaddr;
+ struct ih_idseq {
+ uint16_t icd_id;
+ uint16_t icd_seq;
+ } ih_idseq;
+ uint32_t ih_void;
+
+ struct ih_pmtu {
+ uint16_t ipm_void;
+ uint16_t ipm_nextmtu;
+ } ih_pmtu;
+
+ struct ih_rtradv {
+ uint8_t irt_num_addrs;
+ uint8_t irt_wpa;
+ uint16_t irt_lifetime;
+ } ih_rtradv;
+ } icmp_hun;
+ union {
+ struct {
+ uint32_t its_otime;
+ uint32_t its_rtime;
+ uint32_t its_ttime;
+ } id_ts;
+ struct {
+ struct ip idi_ip;
+ } id_ip;
+ struct icmp_ra_addr id_radv;
+ uint32_t id_mask;
+ uint8_t id_data[1];
+ } icmp_dun;
+};
+
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
+#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
+#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
+
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_radv icmp_dun.id_radv
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_ICMP_H
diff --git a/lib/mlibc/options/posix/include/netinet/tcp.h b/lib/mlibc/options/posix/include/netinet/tcp.h
new file mode 100644
index 0000000..9d64d7a
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/tcp.h
@@ -0,0 +1,37 @@
+#ifndef _NETINET_TCP_H
+#define _NETINET_TCP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Define some macros using same ABI as Linux
+#define TCP_NODELAY 1
+#define TCP_MAXSEG 2
+#define TCP_KEEPIDLE 4
+#define TCP_KEEPINTVL 5
+#define TCP_KEEPCNT 6
+#define TCP_DEFER_ACCEPT 9
+#define TCP_CONGESTION 13
+#define TCP_FASTOPEN 23
+
+#define TCP_ESTABLISHED 1
+#define TCP_SYN_SENT 2
+#define TCP_SYN_RECV 3
+#define TCP_FIN_WAIT1 4
+#define TCP_FIN_WAIT2 5
+#define TCP_TIME_WAIT 6
+#define TCP_CLOSE 7
+#define TCP_CLOSE_WAIT 8
+#define TCP_LAST_ACK 9
+#define TCP_LISTEN 10
+#define TCP_CLOSING 11
+#define TCP_QUICKACK 12
+
+#define SOL_TCP 6
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_TCP_H
diff --git a/lib/mlibc/options/posix/include/netinet/udp.h b/lib/mlibc/options/posix/include/netinet/udp.h
new file mode 100644
index 0000000..5cc887d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/netinet/udp.h
@@ -0,0 +1,31 @@
+#ifndef _NETINET_UDP_H
+#define _NETINET_UDP_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct udphdr {
+ union {
+ struct {
+ uint16_t uh_sport;
+ uint16_t uh_dport;
+ uint16_t uh_ulen;
+ uint16_t uh_sum;
+ };
+ struct {
+ uint16_t source;
+ uint16_t dest;
+ uint16_t len;
+ uint16_t check;
+ };
+ };
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NETINET_UDP_H
diff --git a/lib/mlibc/options/posix/include/nl_types.h b/lib/mlibc/options/posix/include/nl_types.h
new file mode 100644
index 0000000..f0099ba
--- /dev/null
+++ b/lib/mlibc/options/posix/include/nl_types.h
@@ -0,0 +1,6 @@
+#ifndef NL_TYPES_H
+#define NL_TYPES_H
+
+
+
+#endif // NL_TYPES_H \ No newline at end of file
diff --git a/lib/mlibc/options/posix/include/poll.h b/lib/mlibc/options/posix/include/poll.h
new file mode 100644
index 0000000..7550015
--- /dev/null
+++ b/lib/mlibc/options/posix/include/poll.h
@@ -0,0 +1,6 @@
+#ifndef _POLL_H
+#define _POLL_H
+
+#include <sys/poll.h>
+
+#endif // _POLL_H
diff --git a/lib/mlibc/options/posix/include/pthread.h b/lib/mlibc/options/posix/include/pthread.h
new file mode 100644
index 0000000..739f607
--- /dev/null
+++ b/lib/mlibc/options/posix/include/pthread.h
@@ -0,0 +1,325 @@
+
+#ifndef _PTHREAD_H
+#define _PTHREAD_H
+
+#include <abi-bits/clockid_t.h>
+#include <bits/cpu_set.h>
+// TODO: pthread is not required to define size_t.
+#include <bits/size_t.h>
+#include <bits/posix/pthread_t.h>
+#include <bits/threads.h>
+#include <mlibc-config.h>
+
+#include <signal.h>
+#include <stdint.h>
+
+// pthread.h is required to include sched.h and time.h
+#include <sched.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PTHREAD_CREATE_JOINABLE __MLIBC_THREAD_CREATE_JOINABLE
+#define PTHREAD_CREATE_DETACHED __MLIBC_THREAD_CREATE_DETACHED
+
+// Values for pthread_attr_{get,set}scope
+#define PTHREAD_SCOPE_SYSTEM 0
+#define PTHREAD_SCOPE_PROCESS 1
+
+// Values for pthread_attr_{get,set}inheritsched
+#define PTHREAD_INHERIT_SCHED 0
+#define PTHREAD_EXPLICIT_SCHED 1
+
+// values for pthread_{get,set}canceltype().
+#define PTHREAD_CANCEL_DEFERRED 0
+#define PTHREAD_CANCEL_ASYNCHRONOUS 1
+
+// values for pthread_{get,set}cancelstate().
+#define PTHREAD_CANCEL_ENABLE 0
+#define PTHREAD_CANCEL_DISABLE 1
+
+// values for pthread_mutexattr_{get,set}type().
+#define PTHREAD_MUTEX_DEFAULT __MLIBC_THREAD_MUTEX_DEFAULT
+#define PTHREAD_MUTEX_NORMAL __MLIBC_THREAD_MUTEX_NORMAL
+#define PTHREAD_MUTEX_ERRORCHECK __MLIBC_THREAD_MUTEX_ERRORCHECK
+#define PTHREAD_MUTEX_RECURSIVE __MLIBC_THREAD_MUTEX_RECURSIVE
+
+// values for pthread_mutexattr_{get,set}robust().
+#define PTHREAD_MUTEX_STALLED __MLIBC_THREAD_MUTEX_STALLED
+#define PTHREAD_MUTEX_ROBUST __MLIBC_THREAD_MUTEX_ROBUST
+
+// values for pthread_mutexattr_{get,set}pshared().
+#define PTHREAD_PROCESS_PRIVATE __MLIBC_THREAD_PROCESS_PRIVATE
+#define PTHREAD_PROCESS_SHARED __MLIBC_THREAD_PROCESS_SHARED
+
+// Values for pthread_mutexattr_{get,set}protocol()
+#define PTHREAD_PRIO_NONE __MLIBC_THREAD_PRIO_NONE
+#define PTHREAD_PRIO_INHERIT __MLIBC_THREAD_PRIO_INHERIT
+#define PTHREAD_PRIO_PROTECT __MLIBC_THREAD_PRIO_PROTECT
+
+#define PTHREAD_ONCE_INIT {0}
+#define PTHREAD_COND_INITIALIZER {0}
+#define PTHREAD_MUTEX_INITIALIZER {0, 0, 0, 0}
+#define PTHREAD_RWLOCK_INITIALIZER {0, 0, 0}
+
+#define PTHREAD_CANCELED ((void*) -1)
+
+#define PTHREAD_BARRIER_SERIAL_THREAD -1
+
+// values for pthread_key
+#define PTHREAD_DESTRUCTOR_ITERATIONS 8
+
+#define PTHREAD_INHERIT_SCHED 0
+#define PTHREAD_EXPLICIT_SCHED 1
+
+#define PTHREAD_STACK_MIN 16384
+
+#define PTHREAD_ATTR_NO_SIGMASK_NP (-1)
+
+// TODO: move to own file and include in sys/types.h
+typedef struct __mlibc_threadattr pthread_attr_t;
+
+typedef uintptr_t pthread_key_t;
+
+struct __mlibc_once {
+ unsigned int __mlibc_done;
+};
+typedef struct __mlibc_once pthread_once_t;
+
+typedef struct __mlibc_mutexattr pthread_mutexattr_t;
+
+typedef struct __mlibc_mutex pthread_mutex_t;
+
+typedef struct __mlibc_condattr pthread_condattr_t;
+
+typedef struct __mlibc_cond pthread_cond_t;
+
+struct __mlibc_barrierattr_struct {
+ int __mlibc_pshared;
+};
+typedef struct __mlibc_barrierattr_struct pthread_barrierattr_t;
+
+struct __mlibc_barrier {
+ unsigned int __mlibc_waiting;
+ unsigned int __mlibc_inside;
+ unsigned int __mlibc_count;
+ unsigned int __mlibc_seq;
+ unsigned int __mlibc_flags;
+};
+typedef struct __mlibc_barrier pthread_barrier_t;
+
+struct __mlibc_fair_rwlock {
+ unsigned int __mlibc_m; // Mutex.
+ unsigned int __mlibc_rc; // Reader count (not reference count).
+ unsigned int __mlibc_flags;
+};
+typedef struct __mlibc_fair_rwlock pthread_rwlock_t;
+
+struct __mlibc_rwlockattr {
+ int __mlibc_pshared;
+};
+typedef struct __mlibc_rwlockattr pthread_rwlockattr_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+// ----------------------------------------------------------------------------
+// pthread_attr and pthread functions.
+// ----------------------------------------------------------------------------
+
+// pthread_attr functions.
+int pthread_attr_init(pthread_attr_t *);
+int pthread_attr_destroy(pthread_attr_t *);
+
+int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
+int pthread_attr_setdetachstate(pthread_attr_t *, int);
+
+int pthread_attr_getstacksize(const pthread_attr_t *__restrict, size_t *__restrict);
+int pthread_attr_setstacksize(pthread_attr_t *, size_t);
+
+int pthread_attr_getstackaddr(const pthread_attr_t *, void **);
+int pthread_attr_setstackaddr(pthread_attr_t *, void *);
+
+int pthread_attr_getstack(const pthread_attr_t *, void **, size_t*);
+int pthread_attr_setstack(pthread_attr_t *, void *, size_t);
+
+int pthread_attr_getguardsize(const pthread_attr_t *__restrict, size_t *__restrict);
+int pthread_attr_setguardsize(pthread_attr_t *, size_t);
+
+int pthread_attr_getscope(const pthread_attr_t *, int*);
+int pthread_attr_setscope(pthread_attr_t *, int);
+
+int pthread_attr_getschedparam(const pthread_attr_t *__restrict, struct sched_param *__restrict);
+int pthread_attr_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict);
+
+int pthread_attr_getschedpolicy(const pthread_attr_t *__restrict, int *__restrict);
+int pthread_attr_setschedpolicy(pthread_attr_t *__restrict, int);
+
+int pthread_attr_getinheritsched(const pthread_attr_t *__restrict, int *__restrict);
+int pthread_attr_setinheritsched(pthread_attr_t *__restrict, int);
+
+int pthread_attr_getschedparam(const pthread_attr_t *__restrict, struct sched_param *__restrict);
+int pthread_attr_setschedparam(pthread_attr_t *__restrict, const struct sched_param *__restrict);
+
+#if __MLIBC_LINUX_OPTION
+int pthread_attr_getaffinity_np(const pthread_attr_t *__restrict, size_t, cpu_set_t *__restrict);
+int pthread_attr_setaffinity_np(pthread_attr_t *__restrict, size_t, const cpu_set_t *__restrict);
+
+int pthread_attr_getsigmask_np(const pthread_attr_t *__restrict, sigset_t *__restrict);
+int pthread_attr_setsigmask_np(pthread_attr_t *__restrict, const sigset_t *__restrict);
+
+int pthread_getattr_np(pthread_t, pthread_attr_t *);
+
+int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);
+int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);
+#endif /* __MLIBC_LINUX_OPTION */
+
+// pthread functions.
+int pthread_create(pthread_t *__restrict, const pthread_attr_t *__restrict,
+ void *(*) (void *), void *__restrict);
+pthread_t pthread_self(void);
+int pthread_equal(pthread_t, pthread_t);
+__attribute__ ((__noreturn__)) void pthread_exit(void *);
+
+int pthread_join(pthread_t, void **);
+int pthread_detach(pthread_t);
+
+void pthread_cleanup_push(void (*) (void *), void *);
+void pthread_cleanup_pop(int);
+
+int pthread_setname_np(pthread_t, const char *);
+int pthread_getname_np(pthread_t, char *, size_t);
+
+int pthread_attr_setstack(pthread_attr_t *, void *, size_t);
+int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *);
+
+int pthread_getattr_np(pthread_t, pthread_attr_t *);
+
+int pthread_setschedparam(pthread_t, int, const struct sched_param *);
+int pthread_getschedparam(pthread_t, int *, struct sched_param *);
+
+int pthread_setcanceltype(int, int *);
+int pthread_setcancelstate(int, int *);
+void pthread_testcancel(void);
+int pthread_cancel(pthread_t);
+
+int pthread_atfork(void (*) (void), void (*) (void), void (*) (void));
+
+// ----------------------------------------------------------------------------
+// pthread_key functions.
+// ----------------------------------------------------------------------------
+
+int pthread_key_create(pthread_key_t *, void (*) (void *));
+int pthread_key_delete(pthread_key_t);
+
+void *pthread_getspecific(pthread_key_t);
+int pthread_setspecific(pthread_key_t, const void *);
+
+// ----------------------------------------------------------------------------
+// pthread_once functions.
+// ----------------------------------------------------------------------------
+
+int pthread_once(pthread_once_t *, void (*) (void));
+
+// ----------------------------------------------------------------------------
+// pthread_mutexattr and pthread_mutex functions.
+// ----------------------------------------------------------------------------
+
+// pthread_mutexattr functions
+int pthread_mutexattr_init(pthread_mutexattr_t *);
+int pthread_mutexattr_destroy(pthread_mutexattr_t *);
+
+int pthread_mutexattr_gettype(const pthread_mutexattr_t *__restrict, int *__restrict);
+int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
+
+int pthread_mutexattr_getrobust(const pthread_mutexattr_t *__restrict, int *__restrict);
+int pthread_mutexattr_setrobust(pthread_mutexattr_t *, int);
+
+int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *);
+int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int);
+
+int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *__restrict, int *__restrict);
+int pthread_mutexattr_setprotocol(pthread_mutexattr_t *, int);
+
+int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *, int *);
+int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *, int);
+
+// pthread_mutex functions
+int pthread_mutex_init(pthread_mutex_t *__restrict, const pthread_mutexattr_t *__restrict);
+int pthread_mutex_destroy(pthread_mutex_t *);
+
+int pthread_mutex_lock(pthread_mutex_t *);
+int pthread_mutex_trylock(pthread_mutex_t *);
+int pthread_mutex_timedlock(pthread_mutex_t *__restrict,
+ const struct timespec *__restrict);
+int pthread_mutex_unlock(pthread_mutex_t *);
+
+int pthread_mutex_consistent(pthread_mutex_t *);
+
+// ----------------------------------------------------------------------------
+// pthread_condattr and pthread_cond functions.
+// ----------------------------------------------------------------------------
+
+int pthread_condattr_init(pthread_condattr_t *);
+int pthread_condattr_destroy(pthread_condattr_t *);
+
+int pthread_condattr_getclock(const pthread_condattr_t *__restrict, clockid_t *__restrict);
+int pthread_condattr_setclock(pthread_condattr_t *, clockid_t);
+
+int pthread_condattr_getpshared(const pthread_condattr_t *__restrict, int *__restrict);
+int pthread_condattr_setpshared(pthread_condattr_t *, int);
+
+int pthread_cond_init(pthread_cond_t *__restrict, const pthread_condattr_t *__restrict);
+int pthread_cond_destroy(pthread_cond_t *);
+
+int pthread_cond_wait(pthread_cond_t *__restrict, pthread_mutex_t *__restrict);
+int pthread_cond_timedwait(pthread_cond_t *__restrict, pthread_mutex_t *__restrict,
+ const struct timespec *__restrict);
+int pthread_cond_signal(pthread_cond_t *);
+int pthread_cond_broadcast(pthread_cond_t *);
+
+// ----------------------------------------------------------------------------
+// pthread_barrierattr and pthread_barrier functions.
+// ----------------------------------------------------------------------------
+
+int pthread_barrierattr_init(pthread_barrierattr_t *);
+int pthread_barrierattr_destroy(pthread_barrierattr_t *);
+int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int);
+int pthread_barrierattr_getpshared(const pthread_barrierattr_t *__restrict,
+ int *__restrict);
+
+int pthread_barrier_init(pthread_barrier_t *__restrict, const pthread_barrierattr_t *__restrict,
+ unsigned int);
+int pthread_barrier_destroy(pthread_barrier_t *);
+
+int pthread_barrier_wait(pthread_barrier_t *);
+
+// ----------------------------------------------------------------------------
+// pthread_wrlockattr and pthread_rwlock functions.
+// ----------------------------------------------------------------------------
+
+int pthread_rwlockattr_init(pthread_rwlockattr_t *);
+int pthread_rwlockattr_destroy(pthread_rwlockattr_t *);
+int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int);
+int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *__restrict,
+ int *__restrict);
+
+int pthread_rwlock_init(pthread_rwlock_t *__restrict, const pthread_rwlockattr_t *__restrict);
+int pthread_rwlock_destroy(pthread_rwlock_t *);
+int pthread_rwlock_trywrlock(pthread_rwlock_t *);
+int pthread_rwlock_wrlock(pthread_rwlock_t *);
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *);
+int pthread_rwlock_rdlock(pthread_rwlock_t *);
+int pthread_rwlock_unlock(pthread_rwlock_t *);
+
+int pthread_getcpuclockid(pthread_t, clockid_t *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _PTHREAD_H
+
diff --git a/lib/mlibc/options/posix/include/pwd.h b/lib/mlibc/options/posix/include/pwd.h
new file mode 100644
index 0000000..b885f57
--- /dev/null
+++ b/lib/mlibc/options/posix/include/pwd.h
@@ -0,0 +1,45 @@
+
+#ifndef _PWD_H
+#define _PWD_H
+
+#include <abi-bits/uid_t.h>
+#include <abi-bits/gid_t.h>
+#include <bits/size_t.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct passwd {
+ char *pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+};
+
+#define NSS_BUFLEN_PASSWD 512
+
+#ifndef __MLIBC_ABI_ONLY
+
+void endpwent(void);
+struct passwd *getpwent(void);
+struct passwd *getpwnam(const char *);
+int getpwnam_r(const char *, struct passwd *, char *, size_t, struct passwd **);
+struct passwd *getpwuid(uid_t);
+int getpwuid_r(uid_t, struct passwd *, char *, size_t, struct passwd **);
+void setpwent(void);
+int putpwent(const struct passwd *, FILE *);
+struct passwd *fgetpwent(FILE *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _PWD_H
+
diff --git a/lib/mlibc/options/posix/include/regex.h b/lib/mlibc/options/posix/include/regex.h
new file mode 100644
index 0000000..b7f0a46
--- /dev/null
+++ b/lib/mlibc/options/posix/include/regex.h
@@ -0,0 +1,66 @@
+#ifndef _REGEX_H
+#define _REGEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+typedef ptrdiff_t regoff_t;
+
+typedef struct re_pattern_buffer {
+ size_t re_nsub;
+ void *__opaque, *__padding[4];
+ size_t __nsub2;
+ char __padding2;
+} regex_t;
+
+typedef struct {
+ regoff_t rm_so;
+ regoff_t rm_eo;
+} regmatch_t;
+
+// Flags for regcomp().
+#define REG_EXTENDED 1
+#define REG_ICASE 2
+#define REG_NEWLINE 4
+#define REG_NOSUB 8
+
+// Flags for regexec().
+#define REG_NOTBOL 1
+#define REG_NOTEOL 2
+
+// Errors for regcomp() and regexec().
+#define REG_OK 0
+#define REG_NOMATCH 1
+#define REG_BADPAT 2
+#define REG_ECOLLATE 3
+#define REG_ECTYPE 4
+#define REG_EESCAPE 5
+#define REG_ESUBREG 6
+#define REG_EBRACK 7
+#define REG_EPAREN 8
+#define REG_EBRACE 9
+#define REG_BADBR 10
+#define REG_ERANGE 11
+#define REG_ESPACE 12
+#define REG_BADRPT 13
+
+// Obsolete in POSIX.
+#define REG_ENOSYS -1
+
+#ifndef __MLIBC_ABI_ONLY
+
+int regcomp(regex_t *__restrict, const char *__restrict, int);
+int regexec(const regex_t *__restrict, const char *__restrict, size_t, regmatch_t *__restrict, int);
+size_t regerror(int, const regex_t *__restrict, char *__restrict, size_t);
+void regfree(regex_t *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/mlibc/options/posix/include/sched.h b/lib/mlibc/options/posix/include/sched.h
new file mode 100644
index 0000000..739d91e
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sched.h
@@ -0,0 +1,49 @@
+
+#ifndef _SCHED_H
+#define _SCHED_H
+
+#include <abi-bits/pid_t.h>
+#include <bits/threads.h>
+#include <bits/size_t.h>
+#include <mlibc-config.h>
+
+// MISSING: time_t, struct timespec
+
+// MISSING: POSIX [PS], [SS] and [TSP] options
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __MLIBC_LINUX_OPTION
+#include <bits/linux/linux_sched.h>
+#include <bits/linux/cpu_set.h>
+#endif
+
+#define SCHED_OTHER 0
+#define SCHED_FIFO 1
+#define SCHED_RR 2
+#define SCHED_BATCH 3
+#define SCHED_IDLE 5
+#define SCHED_DEADLINE 6
+#define SCHED_RESET_ON_FORK 0x40000000
+
+#ifndef __MLIBC_ABI_ONLY
+
+int sched_yield(void);
+
+int sched_get_priority_max(int policy);
+int sched_get_priority_min(int policy);
+
+int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
+
+int sched_getparam(pid_t pid, struct sched_param *param);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SCHED_H
+
diff --git a/lib/mlibc/options/posix/include/search.h b/lib/mlibc/options/posix/include/search.h
new file mode 100644
index 0000000..02e1913
--- /dev/null
+++ b/lib/mlibc/options/posix/include/search.h
@@ -0,0 +1,37 @@
+
+#ifndef _SEARCH_H
+#define _SEARCH_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ preorder,
+ postorder,
+ endorder,
+ leaf
+} VISIT;
+
+#ifndef __MLIBC_ABI_ONLY
+
+void *tsearch(const void *, void **, int(*compar)(const void *, const void *));
+void *tfind(const void *, void *const *, int (*compar)(const void *, const void *));
+void *tdelete(const void *, void **, int(*compar)(const void *, const void *));
+void twalk(const void *, void (*action)(const void *, VISIT, int));
+void tdestroy(void *, void (*free_node)(void *));
+
+void *lsearch(const void *key, void *base, size_t *nelp, size_t width,
+ int (*compar)(const void *, const void *));
+void *lfind(const void *key, const void *base, size_t *nelp,
+ size_t width, int (*compar)(const void *, const void *));
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SEARCH_H
diff --git a/lib/mlibc/options/posix/include/semaphore.h b/lib/mlibc/options/posix/include/semaphore.h
new file mode 100644
index 0000000..877527f
--- /dev/null
+++ b/lib/mlibc/options/posix/include/semaphore.h
@@ -0,0 +1,37 @@
+#ifndef _SEMAPHORE_H
+#define _SEMAPHORE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/ansi/time_t.h>
+#include <bits/ansi/timespec.h>
+
+#define SEM_VALUE_MAX 0x7FFFFFFF
+#define SEM_FAILED ((sem_t *) 0)
+
+typedef struct sem_ {
+ unsigned int __mlibc_count;
+} sem_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+int sem_init(sem_t *sem, int pshared, unsigned int initial_count);
+sem_t *sem_open(const char *, int, ...);
+int sem_close(sem_t *sem);
+int sem_unlink(const char *);
+int sem_destroy(sem_t *sem);
+int sem_wait(sem_t *sem);
+int sem_trywait(sem_t *sem);
+int sem_timedwait(sem_t *sem, const struct timespec *abstime);
+int sem_post(sem_t *sem);
+int sem_getvalue(sem_t *sem, int *sval);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_SEMAPHORE_H
diff --git a/lib/mlibc/options/posix/include/spawn.h b/lib/mlibc/options/posix/include/spawn.h
new file mode 100644
index 0000000..3ab2004
--- /dev/null
+++ b/lib/mlibc/options/posix/include/spawn.h
@@ -0,0 +1,82 @@
+
+#ifndef _SPAWN_H
+#define _SPAWN_H
+
+#include <abi-bits/signal.h>
+#include <abi-bits/mode_t.h>
+#include <abi-bits/pid_t.h>
+#include <sched.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ int __flags;
+ pid_t __pgrp;
+ sigset_t __def, __mask;
+ int __prio, __pol;
+ void *__fn;
+ char __pad[64 - sizeof(void *)];
+} posix_spawnattr_t;
+
+typedef struct {
+ int __pad0[2];
+ void *__actions;
+ int __pad[16];
+} posix_spawn_file_actions_t;
+
+// MISSIG: sigset_t
+
+struct sched_param;
+
+#define POSIX_SPAWN_RESETIDS 1
+#define POSIX_SPAWN_SETPGROUP 2
+#define POSIX_SPAWN_SETSIGDEF 4
+#define POSIX_SPAWN_SETSIGMASK 8
+#define POSIX_SPAWN_SETSCHEDPARAM 16
+#define POSIX_SPAWN_SETSCHEDULER 32
+#define POSIX_SPAWN_USEVFORK 64
+#define POSIX_SPAWN_SETSID 128
+
+#ifndef __MLIBC_ABI_ONLY
+
+int posix_spawn(pid_t *__restrict pid, const char *__restrict path,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attrs,
+ char *const argv[], char *const envp[]);
+
+int posix_spawnattr_init(posix_spawnattr_t *attr);
+int posix_spawnattr_destroy(posix_spawnattr_t *attr);
+int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags);
+int posix_spawnattr_setsigdefault(posix_spawnattr_t *__restrict attr,
+ const sigset_t *__restrict sigdefault);
+int posix_spawnattr_setschedparam(posix_spawnattr_t *__restrict attr,
+ const struct sched_param *__restrict schedparam);
+int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy);
+int posix_spawnattr_setsigmask(posix_spawnattr_t *__restrict attr,
+ const sigset_t *__restrict sigmask);
+int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup);
+int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
+int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
+int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions,
+ int fildes, int newfildes);
+int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions,
+ int fildes);
+int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *__restrict file_actions,
+ int fildes, const char *__restrict path, int oflag, mode_t mode);
+int posix_spawnp(pid_t *__restrict pid, const char *__restrict file,
+ const posix_spawn_file_actions_t *file_actions,
+ const posix_spawnattr_t *__restrict attrp,
+ char *const argv[], char *const envp[]);
+
+// MISSING: all other functions
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SPAWN_H
+
diff --git a/lib/mlibc/options/posix/include/strings.h b/lib/mlibc/options/posix/include/strings.h
new file mode 100644
index 0000000..a21c3d7
--- /dev/null
+++ b/lib/mlibc/options/posix/include/strings.h
@@ -0,0 +1,32 @@
+
+#ifndef _STRINGS_H
+#define _STRINGS_H
+
+#include <bits/size_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+char *index (const char *s, int c);
+char *rindex(const char *s, int c);
+
+int ffs(int word);
+int strcasecmp(const char *a, const char *b);
+int strncasecmp(const char *a, const char *b, size_t size);
+
+/* Marked as obsolete in posix 2008 but used by at least tracker */
+int bcmp(const void *s1, const void *s2, size_t n);
+void bcopy(const void *s1, void *s2, size_t n);
+void bzero(void *s, size_t n);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _STRINGS_H
+
diff --git a/lib/mlibc/options/posix/include/sys/file.h b/lib/mlibc/options/posix/include/sys/file.h
new file mode 100644
index 0000000..add43d3
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/file.h
@@ -0,0 +1,25 @@
+
+#ifndef _SYS_FILE_H
+#define _SYS_FILE_H
+
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int flock(int, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_FILE_H
+
diff --git a/lib/mlibc/options/posix/include/sys/ipc.h b/lib/mlibc/options/posix/include/sys/ipc.h
new file mode 100644
index 0000000..8318dde
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/ipc.h
@@ -0,0 +1,53 @@
+#ifndef _SYS_IPC_H
+#define _SYS_IPC_H
+
+#include <abi-bits/uid_t.h>
+#include <abi-bits/gid_t.h>
+#include <abi-bits/mode_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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_INFO 3
+
+#define IPC_PRIVATE ((key_t) 0)
+
+#if defined(__aarch64__) || defined(__i386__)
+#define IPC_64 0x100
+#elif defined(__x86_64__) || (defined(__riscv) && __riscv_xlen == 64)
+#define IPC_64 0
+#else
+#error "Unsupported arch!"
+#endif
+
+typedef int key_t;
+
+struct ipc_perm {
+ key_t __ipc_perm_key;
+ uid_t uid;
+ gid_t gid;
+ uid_t cuid;
+ gid_t cgid;
+ mode_t mode;
+ int __ipc_perm_seq;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+key_t ftok(const char *, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/mlibc/options/posix/include/sys/mman.h b/lib/mlibc/options/posix/include/sys/mman.h
new file mode 100644
index 0000000..784878e
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/mman.h
@@ -0,0 +1,47 @@
+#ifndef _SYS_MMAN_H
+#define _SYS_MMAN_H
+
+#include <mlibc-config.h>
+#include <abi-bits/mode_t.h>
+#include <abi-bits/vm-flags.h>
+#include <bits/off_t.h>
+#include <bits/size_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+void *mmap(void *, size_t, int, int, int, off_t);
+int mprotect(void *, size_t, int);
+int munmap(void *, size_t);
+
+int mlock(const void *, size_t);
+int mlockall(int);
+int munlock(const void *, size_t);
+int munlockall(void);
+
+int posix_madvise(void *, size_t, int);
+int msync(void *, size_t, int);
+
+int shm_open(const char *, int, mode_t);
+int shm_unlink(const char *);
+
+// Linux extension:
+void *mremap(void *, size_t, size_t, int, ...);
+int remap_file_pages(void *, size_t, int, size_t, int);
+
+#if __MLIBC_LINUX_OPTION
+int memfd_create(const char *, unsigned int);
+int madvise(void *, size_t, int);
+int mincore(void *, size_t, unsigned char *);
+#endif /* __MLIBC_LINUX_OPTION */
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_MMAN_H
diff --git a/lib/mlibc/options/posix/include/sys/msg.h b/lib/mlibc/options/posix/include/sys/msg.h
new file mode 100644
index 0000000..d602f76
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/msg.h
@@ -0,0 +1,27 @@
+#ifndef _SYS_MSG_H
+#define _SYS_MSG_H
+
+#include <abi-bits/msg.h>
+#include <bits/size_t.h>
+#include <bits/ssize_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int msgget(key_t, int);
+
+int msgctl(int msqid, int cmd, struct msqid_ds *buf);
+
+ssize_t msgrcv(int, void *, size_t, long, int);
+int msgsnd(int, const void *, size_t, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_MSG_H
diff --git a/lib/mlibc/options/posix/include/sys/param.h b/lib/mlibc/options/posix/include/sys/param.h
new file mode 100644
index 0000000..9bb4552
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/param.h
@@ -0,0 +1,36 @@
+
+#ifndef _SYS_PARAM_H
+#define _SYS_PARAM_H
+
+#include <endian.h>
+#include <limits.h>
+
+#define NBBY CHAR_BIT
+#define NGROUPS NGROUPS_MAX
+
+// Report the same value as Linux here.
+#define MAXNAMLEN 255
+#define MAXPATHLEN 4096
+#define HOST_NAME_MAX 64
+#define MAXSYMLINKS 20
+#define MAXHOSTNAMELEN HOST_NAME_MAX
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#undef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#undef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define howmany(x, y) (((x) + ((y) - 1)) / (y))
+
+#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_PARAM_H
+
diff --git a/lib/mlibc/options/posix/include/sys/poll.h b/lib/mlibc/options/posix/include/sys/poll.h
new file mode 100644
index 0000000..3edecab
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/poll.h
@@ -0,0 +1,37 @@
+#ifndef _SYS_POLL_H
+#define _SYS_POLL_H
+
+#include <bits/types.h>
+#include <bits/sigset_t.h>
+#include <bits/ansi/timespec.h>
+#include <abi-bits/poll.h>
+#include <abi-bits/signal.h>
+#include <mlibc-config.h>
+
+typedef __mlibc_size nfds_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int poll(struct pollfd *, nfds_t, int);
+
+#if __MLIBC_LINUX_OPTION
+int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask);
+#endif // __MLIBC_LINUX_OPTION
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_POLL_H
diff --git a/lib/mlibc/options/posix/include/sys/resource.h b/lib/mlibc/options/posix/include/sys/resource.h
new file mode 100644
index 0000000..c5453e2
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/resource.h
@@ -0,0 +1,52 @@
+#ifndef _SYS_RESOURCE_H
+#define _SYS_RESOURCE_H
+
+#include <abi-bits/pid_t.h>
+#include <abi-bits/resource.h>
+#include <bits/posix/id_t.h>
+#include <abi-bits/suseconds_t.h>
+#include <bits/ansi/time_t.h>
+#include <bits/posix/timeval.h>
+
+#define PRIO_PROCESS 1
+#define PRIO_PGRP 2
+#define PRIO_USER 3
+
+#define PRIO_MIN (-20)
+#define PRIO_MAX 20
+
+#define RLIM_INFINITY ((rlim_t)-1)
+#define RLIM_SAVED_MAX ((rlim_t)-1)
+#define RLIM_SAVED_CUR ((rlim_t)-1)
+
+#define RLIM_NLIMITS RLIMIT_NLIMITS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long rlim_t;
+
+struct rlimit {
+ rlim_t rlim_cur;
+ rlim_t rlim_max;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int getpriority(int, id_t);
+int setpriority(int, id_t, int);
+
+int getrusage(int, struct rusage *);
+int getrlimit(int, struct rlimit *);
+int setrlimit(int, const struct rlimit *);
+
+int prlimit(pid_t pid, int resource, const struct rlimit *new_limits, struct rlimit *old_limits);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_RESOURCE_H
diff --git a/lib/mlibc/options/posix/include/sys/select.h b/lib/mlibc/options/posix/include/sys/select.h
new file mode 100644
index 0000000..85a15b0
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/select.h
@@ -0,0 +1,49 @@
+
+#ifndef _SYS_SELECT_H
+#define _SYS_SELECT_H
+
+#include <abi-bits/signal.h>
+
+#include <bits/ansi/time_t.h>
+#include <bits/ansi/timespec.h>
+#include <abi-bits/suseconds_t.h>
+#include <bits/posix/timeval.h>
+#include <bits/posix/fd_set.h>
+
+#define FD_SETSIZE 1024
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef long int __fd_mask;
+#define __NFDBITS (8 * (int) sizeof (__fd_mask))
+
+typedef __fd_mask fd_mask;
+#define NFDBITS __NFDBITS
+
+#ifndef __MLIBC_ABI_ONLY
+
+void __FD_CLR(int fd, fd_set *);
+int __FD_ISSET(int fd, fd_set *);
+void __FD_SET(int fd, fd_set *);
+void __FD_ZERO(fd_set *);
+
+#define FD_CLR(fd, set) __FD_CLR(fd, set)
+#define FD_ISSET(fd, set) __FD_ISSET(fd, set)
+#define FD_SET(fd, set) __FD_SET(fd, set)
+#define FD_ZERO(set) __FD_ZERO(set)
+
+int select(int, fd_set *__restrict, fd_set *__restrict, fd_set *__restrict,
+ struct timeval *__restrict);
+int pselect(int, fd_set *, fd_set *, fd_set *, const struct timespec *,
+ const sigset_t *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_SELECT_H
+
diff --git a/lib/mlibc/options/posix/include/sys/sem.h b/lib/mlibc/options/posix/include/sys/sem.h
new file mode 100644
index 0000000..cb3516a
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/sem.h
@@ -0,0 +1,44 @@
+#ifndef _SYS_SEM_H
+#define _SYS_SEM_H
+
+#include <bits/ansi/time_t.h>
+#include <sys/ipc.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GETALL 13
+#define SETVAL 16
+#define SETALL 17
+
+#define SEM_UNDO 0x1000
+
+struct sembuf {
+ unsigned short int sem_num;
+ short int sem_op;
+ short int sem_flg;
+};
+
+struct semid_ds {
+ struct ipc_perm sem_perm;
+ time_t sem_otime;
+ time_t sem_ctime;
+
+ unsigned long sem_nsems;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int semget(key_t, int, int);
+int semop(int, struct sembuf *, size_t);
+int semctl(int, int, int, ...);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_SEM_H
diff --git a/lib/mlibc/options/posix/include/sys/shm.h b/lib/mlibc/options/posix/include/sys/shm.h
new file mode 100644
index 0000000..3767ced
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/shm.h
@@ -0,0 +1,83 @@
+#ifndef _SYS_SHM_H
+#define _SYS_SHM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <abi-bits/pid_t.h>
+#include <abi-bits/shm.h>
+#include <bits/size_t.h>
+#include <time.h>
+
+#include <sys/ipc.h>
+
+#define SHM_R 0400
+#define SHM_W 0200
+
+#define SHM_RDONLY 010000
+#define SHM_RND 020000
+#define SHM_REMAP 040000
+#define SHM_EXEC 0100000
+
+#define SHM_LOCK 11
+#define SHM_UNLOCK 12
+#define SHM_STAT 13
+#define SHM_INFO 14
+#define SHM_STAT_ANY 15
+#define SHM_DEST 01000
+#define SHM_LOCKED 02000
+#define SHM_HUGETLB 04000
+#define SHM_NORESERVE 010000
+
+#define SHM_HUGE_SHIFT 26
+#define SHM_HUGE_MASK 0x3f
+#define SHM_HUGE_64KB (16 << 26)
+#define SHM_HUGE_512KB (19 << 26)
+#define SHM_HUGE_1MB (20 << 26)
+#define SHM_HUGE_2MB (21 << 26)
+#define SHM_HUGE_8MB (23 << 26)
+#define SHM_HUGE_16MB (24 << 26)
+#define SHM_HUGE_32MB (25 << 26)
+#define SHM_HUGE_256MB (28 << 26)
+#define SHM_HUGE_512MB (29 << 26)
+#define SHM_HUGE_1GB (30 << 26)
+#define SHM_HUGE_2GB (31 << 26)
+#define SHM_HUGE_16GB (34U << 26)
+
+typedef unsigned long shmatt_t;
+
+struct shmid_ds {
+ struct ipc_perm shm_perm;
+ size_t shm_segsz;
+ time_t shm_atime;
+ time_t shm_dtime;
+ time_t shm_ctime;
+ pid_t shm_cpid;
+ pid_t shm_lpid;
+ unsigned long shm_nattch;
+};
+
+struct shminfo {
+ unsigned long shmmax;
+ unsigned long shmmin;
+ unsigned long shmmni;
+ unsigned long shmseg;
+ unsigned long shmall;
+ unsigned long __unused[4];
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+void *shmat(int, const void *, int);
+int shmctl(int, int, struct shmid_ds *);
+int shmdt(const void *);
+int shmget(key_t, size_t, int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_SHM_H
diff --git a/lib/mlibc/options/posix/include/sys/socket.h b/lib/mlibc/options/posix/include/sys/socket.h
new file mode 100644
index 0000000..9552f93
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/socket.h
@@ -0,0 +1,105 @@
+
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+#include <abi-bits/gid_t.h>
+#include <abi-bits/pid_t.h>
+#include <bits/size_t.h>
+#include <abi-bits/socklen_t.h>
+#include <bits/ssize_t.h>
+#include <abi-bits/uid_t.h>
+#include <bits/posix/iovec.h>
+#include <abi-bits/socket.h>
+#include <bits/ansi/time_t.h>
+#include <bits/ansi/timespec.h>
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sockaddr {
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+// Control message format:
+// The offsets marked with ^ are aligned to alignof(size_t).
+//
+// |---HEADER---|---DATA---|---PADDING---|---HEADER---|...
+// ^ ^ ^
+// |---------CMSG_LEN------|
+// |---------------CMSG_SPACE------------|
+
+// Auxiliary macro. While there is basically no reason for applications
+// to use this, it is exported by glibc.
+#define CMSG_ALIGN(s) (((s) + __alignof__(size_t) - 1) & \
+ ~(__alignof__(size_t) - 1))
+
+// Basic macros to return content and padding size of a control message.
+#define CMSG_LEN(s) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (s))
+#define CMSG_SPACE(s) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(s))
+
+// Provides access to the data of a control message.
+#define CMSG_DATA(c) ((char *)(c) + CMSG_ALIGN(sizeof(struct cmsghdr)))
+
+#define __MLIBC_CMSG_NEXT(c) ((char *)(c) + CMSG_ALIGN((c)->cmsg_len))
+#define __MLIBC_MHDR_LIMIT(m) ((char *)(m)->msg_control + (m)->msg_controllen)
+
+// For parsing control messages only.
+// Returns a pointer to the first header or nullptr if there is none.
+#define CMSG_FIRSTHDR(m) ((size_t)(m)->msg_controllen <= sizeof(struct cmsghdr) \
+ ? (struct cmsghdr *)0 : (struct cmsghdr *) (m)->msg_control)
+
+// For parsing control messages only.
+// Returns a pointer to the next header or nullptr if there is none.
+#define CMSG_NXTHDR(m, c) \
+ ((c)->cmsg_len < sizeof(struct cmsghdr) || \
+ (ptrdiff_t)(sizeof(struct cmsghdr) + CMSG_ALIGN((c)->cmsg_len)) \
+ >= __MLIBC_MHDR_LIMIT(m) - (char *)(c) \
+ ? (struct cmsghdr *)0 : (struct cmsghdr *)__MLIBC_CMSG_NEXT(c))
+
+struct linger{
+ int l_onoff;
+ int l_linger;
+};
+
+struct ucred {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int accept(int, struct sockaddr *__restrict, socklen_t *__restrict);
+int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int);
+int bind(int, const struct sockaddr *, socklen_t);
+int connect(int, const struct sockaddr *, socklen_t);
+int getpeername(int, struct sockaddr *__restrict, socklen_t *__restrict);
+int getsockname(int, struct sockaddr *__restrict, socklen_t *__restrict);
+int getsockopt(int, int, int, void *__restrict, socklen_t *__restrict);
+int listen(int, int);
+ssize_t recv(int, void *, size_t, int);
+ssize_t recvfrom(int, void *__restrict, size_t, int, struct sockaddr *__restrict, socklen_t *__restrict);
+ssize_t recvmsg(int, struct msghdr *, int);
+ssize_t send(int, const void *, size_t, int);
+ssize_t sendmsg(int, const struct msghdr *, int);
+ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
+int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);
+int sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags);
+int setsockopt(int, int, int, const void *, socklen_t);
+int shutdown(int, int);
+int sockatmark(int);
+int socket(int, int, int);
+int socketpair(int, int, int, int [2]);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _UNISTD_H
+
diff --git a/lib/mlibc/options/posix/include/sys/stat.h b/lib/mlibc/options/posix/include/sys/stat.h
new file mode 100644
index 0000000..7159a77
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/stat.h
@@ -0,0 +1,37 @@
+
+#ifndef _SYS_STAT_H
+#define _SYS_STAT_H
+
+#include <bits/posix/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int chmod(const char *, mode_t);
+int fchmod(int, mode_t);
+int fchmodat(int, const char *, mode_t, int);
+int fstat(int fd, struct stat *result);
+int fstatat(int, const char *__restrict, struct stat *__restrict, int);
+int futimens(int fd, const struct timespec times[2]);
+int lstat(const char *__restrict, struct stat *__restrict);
+int mkdir(const char *, mode_t);
+int mkdirat(int, const char *, mode_t);
+int mkfifo(const char *, mode_t);
+int mkfifoat(int, const char *, mode_t);
+int mknod(const char *, mode_t, dev_t);
+int mknodat(int, const char *, mode_t, dev_t);
+int stat(const char *__restrict, struct stat *__restrict);
+mode_t umask(mode_t);
+int utimensat(int, const char *, const struct timespec times[2], int);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_STAT_H
+
diff --git a/lib/mlibc/options/posix/include/sys/statvfs.h b/lib/mlibc/options/posix/include/sys/statvfs.h
new file mode 100644
index 0000000..0e4c308
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/statvfs.h
@@ -0,0 +1,22 @@
+#ifndef _SYS_STATVFS_H
+#define _SYS_STATVFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <abi-bits/statvfs.h>
+
+#ifndef __MLIBC_ABI_ONLY
+
+int statvfs(const char *__restrict, struct statvfs *__restrict);
+int fstatvfs(int, struct statvfs *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_STATVFS_H
+
diff --git a/lib/mlibc/options/posix/include/sys/syslog.h b/lib/mlibc/options/posix/include/sys/syslog.h
new file mode 100644
index 0000000..7761ece
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/syslog.h
@@ -0,0 +1 @@
+#include <syslog.h>
diff --git a/lib/mlibc/options/posix/include/sys/termios.h b/lib/mlibc/options/posix/include/sys/termios.h
new file mode 100644
index 0000000..b23f171
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/termios.h
@@ -0,0 +1,6 @@
+
+#ifndef _SYS_TERMIOS_H
+#define _SYS_TERMIOS_H
+#include <termios.h>
+#endif // _SYS_TERMIOS_H
+
diff --git a/lib/mlibc/options/posix/include/sys/time.h b/lib/mlibc/options/posix/include/sys/time.h
new file mode 100644
index 0000000..838d7cc
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/time.h
@@ -0,0 +1,68 @@
+#ifndef _SYS_TIME_H
+#define _SYS_TIME_H
+
+#include <abi-bits/time.h>
+#include <abi-bits/signal.h>
+#include <abi-bits/clockid_t.h>
+#include <bits/ansi/time_t.h>
+#include <abi-bits/suseconds_t.h>
+#include <bits/posix/timer_t.h>
+#include <bits/posix/timeval.h>
+
+#include <sys/select.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+// TODO: this function is [OB]. disable it by default and add a macro to enable it
+int gettimeofday(struct timeval *__restrict result, void *__restrict unused);
+int settimeofday(const struct timeval *result, const struct timezone *zone);
+
+void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *res);
+void timersub(const struct timeval *a, const struct timeval *b, struct timeval *res);
+void timerclear(struct timeval *tvp);
+int timerisset(struct timeval *tvp);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+// timercmp taken from musl
+#define timercmp(s,t,op) ((s)->tv_sec == (t)->tv_sec ? \
+ (s)->tv_usec op (t)->tv_usec : (s)->tv_sec op (t)->tv_sec)
+
+#ifndef __MLIBC_ABI_ONLY
+
+int getitimer(int which, struct itimerval *curr_value);
+int setitimer(int which, const struct itimerval *new_value,
+ struct itimerval *old_value);
+
+int timer_create(clockid_t clockid, struct sigevent *__restrict sevp, timer_t *__restrict timerid);
+int timer_settime(timer_t timerid, int flags, const struct itimerspec *__restrict new_value,
+ struct itimerspec *__restrict old_value);
+int timer_gettime(timer_t timerid, struct itimerspec *curr_value);
+int timer_delete(timer_t timerid);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+// The following 2 macros are taken from musl
+#define TIMEVAL_TO_TIMESPEC(tv, ts) ( \
+ (ts)->tv_sec = (tv)->tv_sec, \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000, \
+ (void)0 )
+#define TIMESPEC_TO_TIMEVAL(tv, ts) ( \
+ (tv)->tv_sec = (ts)->tv_sec, \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000, \
+ (void)0 )
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_TIME_H
diff --git a/lib/mlibc/options/posix/include/sys/times.h b/lib/mlibc/options/posix/include/sys/times.h
new file mode 100644
index 0000000..2dd2889
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/times.h
@@ -0,0 +1,28 @@
+#ifndef _SYS_TIMES_H
+#define _SYS_TIMES_H
+
+// TODO: Only define the clock_t type.
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tms {
+ clock_t tms_utime;
+ clock_t tms_stime;
+ clock_t tms_cutime;
+ clock_t tms_cstime;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+clock_t times(struct tms *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_TIMES_H
diff --git a/lib/mlibc/options/posix/include/sys/ttydefaults.h b/lib/mlibc/options/posix/include/sys/ttydefaults.h
new file mode 100644
index 0000000..c6d04f6
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/ttydefaults.h
@@ -0,0 +1,39 @@
+
+#ifndef _SYS_TTYDEFAULTS_H
+#define _SYS_TTYDEFAULTS_H
+
+// Values taken from musl
+
+#define TTYDEF_IFLAG (BRKINT | ISTRIP | ICRNL | IMAXBEL | IXON | IXANY)
+#define TTYDEF_OFLAG (OPOST | ONLCR | XTABS)
+#define TTYDEF_LFLAG (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL)
+#define TTYDEF_CFLAG (CREAD | CS7 | PARENB | HUPCL)
+#define TTYDEF_SPEED (B9600)
+
+#define CTRL(x) ((x) & 037)
+#define CEOF CTRL('d')
+
+#define CEOL '\0'
+#define CEOL2 '\0'
+#define CSTATUS '\0'
+
+#define CERASE 0177
+#define CINTR CTRL('c')
+#define CKILL CTRL('u')
+#define CMIN 1
+#define CQUIT 034
+#define CSUSP CTRL('z')
+#define CTIME 0
+#define CDSUSP CTRL('y')
+#define CSTART CTRL('q')
+#define CSTOP CTRL('s')
+#define CLNEXT CTRL('v')
+#define CDISCARD CTRL('o')
+#define CWERASE CTRL('w')
+#define CREPRINT CTRL('r')
+#define CEOT CEOF
+#define CBRK CEOL
+#define CRPRNT CREPRINT
+#define CFLUSH CDISCARD
+
+#endif // _SYS_TTYDEFAULTS_H
diff --git a/lib/mlibc/options/posix/include/sys/types.h b/lib/mlibc/options/posix/include/sys/types.h
new file mode 100644
index 0000000..ad837fc
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/types.h
@@ -0,0 +1,53 @@
+
+#ifndef _SYS_TYPES_H
+#define _SYS_TYPES_H
+
+#include <bits/size_t.h>
+#include <bits/ssize_t.h>
+#include <bits/off_t.h>
+
+#include <bits/posix/id_t.h>
+#include <abi-bits/uid_t.h>
+#include <abi-bits/gid_t.h>
+#include <abi-bits/pid_t.h>
+
+#include <abi-bits/mode_t.h>
+#include <abi-bits/dev_t.h>
+#include <abi-bits/ino_t.h>
+#include <abi-bits/blksize_t.h>
+#include <abi-bits/blkcnt_t.h>
+#include <abi-bits/nlink_t.h>
+
+#include <bits/ansi/time_t.h>
+#include <abi-bits/suseconds_t.h>
+
+#include <abi-bits/fsblkcnt_t.h>
+#include <abi-bits/fsfilcnt_t.h>
+#include <bits/posix/fd_set.h>
+
+#include <stdint.h>
+
+#include <sys/select.h>
+
+typedef unsigned int u_int;
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned long int u_long;
+typedef char *caddr_t;
+typedef off64_t loff_t;
+
+typedef unsigned long int ulong;
+typedef unsigned short int ushort;
+typedef unsigned int uint;
+
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+
+// BSD extensions
+typedef int64_t quad_t;
+typedef uint64_t u_quad_t;
+
+#endif // _SYS_TYPES_H
+
diff --git a/lib/mlibc/options/posix/include/sys/uio.h b/lib/mlibc/options/posix/include/sys/uio.h
new file mode 100644
index 0000000..04679a6
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/uio.h
@@ -0,0 +1,31 @@
+#ifndef _SYS_UIO_H
+#define _SYS_UIO_H
+
+#include <bits/posix/iovec.h>
+#include <bits/ssize_t.h>
+#include <bits/off_t.h>
+#include <bits/size_t.h>
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UIO_MAXIOV IOV_MAX
+
+#ifndef __MLIBC_ABI_ONLY
+
+ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
+ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
+
+// Non standard extensions, also found on modern BSD's
+ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
+ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_UIO_H
diff --git a/lib/mlibc/options/posix/include/sys/un.h b/lib/mlibc/options/posix/include/sys/un.h
new file mode 100644
index 0000000..bb9b5ad
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/un.h
@@ -0,0 +1,24 @@
+
+#ifndef _SYS_UN_H
+#define _SYS_UN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <abi-bits/socket.h>
+
+struct sockaddr_un {
+ sa_family_t sun_family;
+ char sun_path[108];
+};
+
+// Evaluate to actual length of the `sockaddr_un' structure.
+#define SUN_LEN(ptr) ((size_t) offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_UN_H
+
diff --git a/lib/mlibc/options/posix/include/sys/utsname.h b/lib/mlibc/options/posix/include/sys/utsname.h
new file mode 100644
index 0000000..bd7b174
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/utsname.h
@@ -0,0 +1,22 @@
+
+#ifndef _SYS_UTSNAME_H
+#define _SYS_UTSNAME_H
+
+#include <abi-bits/utsname.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int uname(struct utsname *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_UTSNAME_H
+
diff --git a/lib/mlibc/options/posix/include/sys/wait.h b/lib/mlibc/options/posix/include/sys/wait.h
new file mode 100644
index 0000000..5081041
--- /dev/null
+++ b/lib/mlibc/options/posix/include/sys/wait.h
@@ -0,0 +1,40 @@
+
+#ifndef _SYS_WAIT_H
+#define _SYS_WAIT_H
+
+#include <bits/posix/id_t.h>
+#include <abi-bits/pid_t.h>
+// for siginfo_t
+#include <abi-bits/signal.h>
+#include <abi-bits/wait.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// According to POSIX, <sys/wait.h> does not make rusage available.
+struct rusage;
+
+// TODO: move to own file and include in sys/types.h
+typedef enum {
+ P_ALL, P_PID, P_PGID
+} idtype_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+pid_t wait(int *status);
+int waitid(idtype_t idtype, id_t id, siginfo_t *siginfo, int flags);
+pid_t waitpid(pid_t pid, int *status, int flags);
+
+// GNU extensions.
+pid_t wait3(int *, int, struct rusage *);
+pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYS_WAIT_H
+
diff --git a/lib/mlibc/options/posix/include/syslog.h b/lib/mlibc/options/posix/include/syslog.h
new file mode 100644
index 0000000..6c258cf
--- /dev/null
+++ b/lib/mlibc/options/posix/include/syslog.h
@@ -0,0 +1,75 @@
+
+#ifndef _SYSLOG_H
+#define _SYSLOG_H
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOG_PID 0x01
+#define LOG_CONS 0x02
+#define LOG_NDELAY 0x08
+#define LOG_ODELAY 0x04
+#define LOG_NOWAIT 0x10
+#define LOG_PERROR 0x20
+
+#define LOG_KERN (0<<3)
+#define LOG_USER (1<<3)
+#define LOG_MAIL (2<<3)
+#define LOG_DAEMON (3<<3)
+#define LOG_AUTH (4<<3)
+#define LOG_SYSLOG (5<<3)
+#define LOG_LPR (6<<3)
+#define LOG_NEWS (7<<3)
+#define LOG_UUCP (8<<3)
+#define LOG_CRON (9<<3)
+#define LOG_AUTHPRIV (10<<3)
+#define LOG_FTP (11<<3)
+
+#define LOG_LOCAL0 (16<<3)
+#define LOG_LOCAL1 (17<<3)
+#define LOG_LOCAL2 (18<<3)
+#define LOG_LOCAL3 (19<<3)
+#define LOG_LOCAL4 (20<<3)
+#define LOG_LOCAL5 (21<<3)
+#define LOG_LOCAL6 (22<<3)
+#define LOG_LOCAL7 (23<<3)
+
+#define LOG_PRIMASK 7
+#define LOG_PRI(p) ((p)&LOG_PRIMASK)
+#define LOG_MAKEPRI(f, p) (((f)<<3) | (p))
+#define LOG_MASK(p) (1<<(p))
+#define LOG_UPTO(p) ((1<<((p)+1))-1)
+#define LOG_NFACILITIES 24
+#define LOG_FACMASK (0x7F<<3)
+#define LOG_FAC(p) (((p)&LOG_FACMASK)>>3)
+
+#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
+
+#ifndef __MLIBC_ABI_ONLY
+
+void closelog(void);
+void openlog(const char *, int, int);
+int setlogmask(int);
+void syslog(int, const char *, ...);
+
+// This is a linux extension
+void vsyslog(int, const char *, va_list);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _SYSLOG_H
+
diff --git a/lib/mlibc/options/posix/include/termios.h b/lib/mlibc/options/posix/include/termios.h
new file mode 100644
index 0000000..a5a6a2f
--- /dev/null
+++ b/lib/mlibc/options/posix/include/termios.h
@@ -0,0 +1,100 @@
+
+#ifndef _TERMIOS_H
+#define _TERMIOS_H
+
+#include <abi-bits/pid_t.h>
+#include <abi-bits/termios.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/winsize.h>
+
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+#include <sys/ttydefaults.h>
+#endif
+
+// baud rate constants for speed_t
+#define B0 0
+#define B50 1
+#define B75 2
+#define B110 3
+#define B134 4
+#define B150 5
+#define B200 6
+#define B300 7
+#define B600 8
+#define B1200 9
+#define B1800 10
+#define B2400 11
+#define B4800 12
+#define B9600 13
+#define B19200 14
+#define B38400 15
+#define B57600 0010001
+#define B115200 0010002
+#define B230400 0010003
+#define B460800 0010004
+#define B500000 0010005
+#define B576000 0010006
+#define B921600 0010007
+#define B1000000 0010010
+#define B1152000 0010011
+#define B1500000 0010012
+#define B2000000 0010013
+#define B2500000 0010014
+#define B3000000 0010015
+#define B3500000 0010016
+#define B4000000 0010017
+
+// constants for tcsetattr()
+#define TCSANOW 0
+#define TCSADRAIN 1
+#define TCSAFLUSH 2
+
+// constants for tcflush()
+#define TCIFLUSH 0
+#define TCOFLUSH 1
+#define TCIOFLUSH 2
+
+// constants for tcflow()
+#define TCOOFF 0
+#define TCOON 1
+#define TCIOFF 2
+#define TCION 3
+
+#define TIOCM_DTR 0x002
+#define TIOCM_RTS 0x004
+
+#ifndef __MLIBC_ABI_ONLY
+
+speed_t cfgetispeed(const struct termios *);
+speed_t cfgetospeed(const struct termios *);
+int cfsetispeed(struct termios *, speed_t);
+int cfsetospeed(struct termios *, speed_t);
+void cfmakeraw(struct termios *);
+int tcdrain(int);
+int tcflow(int, int);
+int tcflush(int, int);
+int tcgetattr(int fd, struct termios *attr);
+pid_t tcgetsid(int);
+int tcsendbreak(int, int);
+int tcsetattr(int, int, const struct termios *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+// This is a linux extension
+
+#define TIOCGPGRP 0x540F
+#define TIOCSPGRP 0x5410
+#define TIOCGWINSZ 0x5413
+#define TIOCSWINSZ 0x5414
+#define TIOCGSID 0x5429
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _TERMIOS_H
+
diff --git a/lib/mlibc/options/posix/include/ucontext.h b/lib/mlibc/options/posix/include/ucontext.h
new file mode 100644
index 0000000..c50b0b1
--- /dev/null
+++ b/lib/mlibc/options/posix/include/ucontext.h
@@ -0,0 +1,23 @@
+#ifndef _UCONTEXT_H
+#define _UCONTEXT_H
+
+#include <signal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#ifndef __MLIBC_ABI_ONLY
+
+int getcontext(ucontext_t *);
+int setcontext(const ucontext_t *);
+void makecontext(ucontext_t *, void (*)(void), int, ...);
+int swapcontext(ucontext_t *, const ucontext_t *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // _UCONTEXT_H
diff --git a/lib/mlibc/options/posix/include/unistd.h b/lib/mlibc/options/posix/include/unistd.h
new file mode 100644
index 0000000..d29257d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/unistd.h
@@ -0,0 +1,360 @@
+
+#ifndef _UNISTD_H
+#define _UNISTD_H
+
+#include <mlibc-config.h>
+#include <bits/types.h>
+#include <bits/size_t.h>
+#include <bits/ssize_t.h>
+#include <bits/off_t.h>
+#include <bits/types.h>
+#include <abi-bits/access.h>
+#include <abi-bits/uid_t.h>
+#include <abi-bits/gid_t.h>
+#include <abi-bits/pid_t.h>
+#include <abi-bits/seek-whence.h>
+
+#if __MLIBC_SYSDEP_HAS_BITS_SYSCALL_H && __MLIBC_LINUX_OPTION
+#include <bits/syscall.h>
+#endif /* __MLIBC_SYSDEP_HAS_BITS_SYSCALL_H && __MLIBC_LINUX_OPTION */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _POSIX_VERSION 200809L
+#define _POSIX2_VERSION _POSIX_VERSION
+#define _XOPEN_VERSION 700
+
+#define _POSIX_FSYNC _POSIX_VERSION
+#define _POSIX_IPV6 _POSIX_VERSION
+#define _POSIX_JOB_CONTROL 1
+#define _POSIX_SAVED_IDS 1
+#define _POSIX_SHELL 1
+#define _POSIX_SPAWN _POSIX_VERSION
+#define _POSIX_THREADS _POSIX_VERSION
+#define _POSIX_THREAD_SAFE_FUNCTIONS _POSIX_VERSION
+#define _POSIX_MONOTONIC_CLOCK 0
+
+#if __MLIBC_CRYPT_OPTION
+#define _XOPEN_CRYPT 1
+#endif
+
+// MISSING: additional _POSIX and _XOPEN feature macros
+// MISSING: _POSIX_TIMESTAMP_RESOLUTION and _POSIX2_SYMLINKS
+
+#define _CS_PATH 0
+#define _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS 1
+#define _CS_GNU_LIBC_VERSION 2
+#define _CS_GNU_LIBPTHREAD_VERSION 3
+#define _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS 4
+#define _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS 5
+
+#define _CS_POSIX_V6_ILP32_OFF32_CFLAGS 1116
+#define _CS_POSIX_V6_ILP32_OFF32_LDFLAGS 1117
+#define _CS_POSIX_V6_ILP32_OFF32_LIBS 1118
+#define _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS 1119
+#define _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS 1120
+#define _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS 1121
+#define _CS_POSIX_V6_ILP32_OFFBIG_LIBS 1122
+#define _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS 1123
+#define _CS_POSIX_V6_LP64_OFF64_CFLAGS 1124
+#define _CS_POSIX_V6_LP64_OFF64_LDFLAGS 1125
+#define _CS_POSIX_V6_LP64_OFF64_LIBS 1126
+#define _CS_POSIX_V6_LP64_OFF64_LINTFLAGS 1127
+#define _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS 1128
+#define _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS 1129
+#define _CS_POSIX_V6_LPBIG_OFFBIG_LIBS 1130
+#define _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS 1131
+#define _CS_POSIX_V7_ILP32_OFF32_CFLAGS 1132
+#define _CS_POSIX_V7_ILP32_OFF32_LDFLAGS 1133
+#define _CS_POSIX_V7_ILP32_OFF32_LIBS 1134
+#define _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS 1135
+#define _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS 1136
+#define _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS 1137
+#define _CS_POSIX_V7_ILP32_OFFBIG_LIBS 1138
+#define _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS 1139
+#define _CS_POSIX_V7_LP64_OFF64_CFLAGS 1140
+#define _CS_POSIX_V7_LP64_OFF64_LDFLAGS 1141
+#define _CS_POSIX_V7_LP64_OFF64_LIBS 1142
+#define _CS_POSIX_V7_LP64_OFF64_LINTFLAGS 1143
+#define _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS 1144
+#define _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS 1145
+#define _CS_POSIX_V7_LPBIG_OFFBIG_LIBS 1146
+#define _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS 1147
+#define _CS_V6_ENV 1148
+#define _CS_V7_ENV 1149
+
+// MISSING: SEEK macros from stdio.h
+
+#define F_LOCK 1
+#define F_TEST 2
+#define F_TLOCK 3
+#define F_ULOCK 4
+
+// MISSING: _PC macros
+// For now, use the Linux ABI for _PC constants.
+#define _PC_LINK_MAX 0
+#define _PC_MAX_CANON 1
+#define _PC_MAX_INPUT 2
+#define _PC_NAME_MAX 3
+#define _PC_PATH_MAX 4
+#define _PC_PIPE_BUF 5
+#define _PC_CHOWN_RESTRICTED 6
+#define _PC_NO_TRUNC 7
+#define _PC_VDISABLE 8
+
+#define _PC_FILESIZEBITS 9
+#define _PC_SYMLINK_MAX 10
+
+// MISSING: remaining _SC_macros
+#define _SC_ARG_MAX 0
+#define _SC_GETPW_R_SIZE_MAX 1
+#define _SC_PHYS_PAGES 2
+#define _SC_PAGE_SIZE 3
+#define _SC_PAGESIZE _SC_PAGE_SIZE
+#define _SC_OPEN_MAX 5
+#define _SC_NPROCESSORS_ONLN 6
+#define _SC_GETGR_R_SIZE_MAX 7
+
+#define _SC_CHILD_MAX 8
+#define _SC_CLK_TCK 9
+#define _SC_NGROUPS_MAX 10
+#define _SC_VERSION 11
+#define _SC_SAVED_IDS 12
+#define _SC_JOB_CONTROL 13
+#define _SC_HOST_NAME_MAX 14
+#define _SC_LINE_MAX 15
+#define _SC_XOPEN_CRYPT 16
+#define _SC_NPROCESSORS_CONF 17
+#define _SC_SYMLOOP_MAX 18
+#define _SC_TTY_NAME_MAX 19
+#define _SC_RE_DUP_MAX 20
+
+#define _SC_ATEXIT_MAX 21
+#define _SC_LOGIN_NAME_MAX 22
+#define _SC_THREAD_DESTRUCTOR_ITERATIONS 23
+#define _SC_THREAD_KEYS_MAX 24
+#define _SC_THREAD_STACK_MIN 25
+#define _SC_THREAD_THREADS_MAX 26
+#define _SC_TZNAME_MAX 27
+#define _SC_ASYNCHRONOUS_IO 28
+#define _SC_FSYNC 29
+#define _SC_MAPPED_FILES 30
+#define _SC_MEMLOCK 31
+#define _SC_MEMLOCK_RANGE 32
+#define _SC_MEMORY_PROTECTION 33
+#define _SC_MESSAGE_PASSING 34
+#define _SC_PRIORITY_SCHEDULING 35
+#define _SC_REALTIME_SIGNALS 36
+#define _SC_SEMAPHORES 37
+#define _SC_SHARED_MEMORY_OBJECTS 38
+#define _SC_SYNCHRONIZED_IO 39
+#define _SC_THREADS 40
+#define _SC_THREAD_ATTR_STACKADDR 41
+#define _SC_THREAD_ATTR_STACKSIZE 42
+#define _SC_THREAD_PRIORITY_SCHEDULING 43
+#define _SC_THREAD_PRIO_INHERIT 44
+#define _SC_THREAD_PRIO_PROTECT 45
+#define _SC_THREAD_PROCESS_SHARED 46
+#define _SC_THREAD_SAFE_FUNCTIONS 47
+#define _SC_TIMERS 48
+#define _SC_TIMER_MAX 49
+#define _SC_2_CHAR_TERM 50
+#define _SC_2_C_BIND 51
+#define _SC_2_C_DEV 52
+#define _SC_2_FORT_DEV 53
+#define _SC_2_FORT_RUN 54
+#define _SC_2_LOCALEDEF 55
+#define _SC_2_SW_DEV 56
+#define _SC_2_UPE 57
+#define _SC_2_VERSION 58
+#define _SC_CLOCK_SELECTION 59
+#define _SC_CPUTIME 60
+#define _SC_THREAD_CPUTIME 61
+#define _SC_MONOTONIC_CLOCK 62
+#define _SC_READER_WRITER_LOCKS 63
+#define _SC_SPIN_LOCKS 64
+#define _SC_REGEXP 65
+#define _SC_SHELL 66
+#define _SC_SPAWN 67
+#define _SC_2_PBS 68
+#define _SC_2_PBS_ACCOUNTING 69
+#define _SC_2_PBS_LOCATE 70
+#define _SC_2_PBS_TRACK 71
+#define _SC_2_PBS_MESSAGE 72
+#define _SC_STREAM_MAX 73
+#define _SC_AIO_LISTIO_MAX 74
+#define _SC_AIO_MAX 75
+#define _SC_DELAYTIMER_MAX 76
+#define _SC_MQ_OPEN_MAX 77
+#define _SC_MQ_PRIO_MAX 78
+#define _SC_RTSIG_MAX 79
+#define _SC_SIGQUEUE_MAX 80
+#define _SC_IOV_MAX 81
+
+#define STDERR_FILENO 2
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+
+#define _POSIX_VDISABLE '\0'
+
+#define L_ctermid 20
+
+#ifndef intptr_t
+typedef __mlibc_intptr intptr_t;
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int access(const char *path, int mode);
+unsigned int alarm(unsigned int seconds);
+int chdir(const char *path);
+int chown(const char *path, uid_t uid, gid_t gid);
+int close(int fd);
+ssize_t confstr(int, char *, size_t);
+char *ctermid(char *s);
+int dup(int fd);
+int dup2(int src_fd, int dest_fd);
+__attribute__((__noreturn__)) void _exit(int status);
+void endusershell(void);
+int execl(const char *, const char *, ...);
+int execle(const char *, const char *, ...);
+int execlp(const char *, const char *, ...);
+int execv(const char *, char *const []);
+int execve(const char *path, char *const argv[], char *const envp[]);
+int execvp(const char *, char *const[]);
+int execvpe(const char *path, char *const argv[], char *const envp[]);
+int faccessat(int, const char *, int, int);
+int fchdir(int fd);
+int fchown(int fd, uid_t uid, gid_t gid);
+int fchownat(int fd, const char *path, uid_t uid, gid_t gid, int flags);
+int fdatasync(int);
+int fexecve(int, char *const [], char *const []);
+pid_t fork(void);
+pid_t vfork(void);
+long fpathconf(int, int);
+int fsync(int);
+int ftruncate(int, off_t);
+char *getcwd(char *, size_t);
+gid_t getegid(void);
+uid_t geteuid(void);
+gid_t getgid(void);
+int getgroups(int, gid_t []);
+long gethostid(void);
+int gethostname(char *buffer, size_t max_length);
+int sethostname(const char *buffer, size_t max_length);
+char *getlogin(void);
+int getlogin_r(char *, size_t);
+int getopt(int, char *const [], const char *);
+char *getpass(const char *);
+pid_t getpgid(pid_t);
+pid_t getpgrp(void);
+pid_t getpid(void);
+pid_t getppid(void);
+pid_t getsid(pid_t);
+uid_t getuid(void);
+char *getusershell(void);
+int isatty(int fd);
+int lchown(const char *path, uid_t uid, gid_t gid);
+int link(const char *, const char *);
+int linkat(int, const char *, int, const char *, int);
+int lockf(int, int, off_t);
+off_t lseek(int fd, off_t offset, int whence);
+off64_t lseek64(int fd, off64_t offset, int whence);
+int nice(int);
+long pathconf(const char *, int);
+int pause(void);
+int pipe(int [2]);
+ssize_t pread(int, void *, size_t, off_t);
+ssize_t pwrite(int, const void *, size_t, off_t);
+ssize_t read(int fd, void *buffer, size_t size);
+ssize_t readlink(const char *__restrict, char *__restrict, size_t);
+ssize_t readlinkat(int, const char *__restrict, char *__restrict, size_t);
+int rmdir(const char *);
+int setegid(gid_t);
+int seteuid(uid_t);
+int setgid(gid_t);
+int setpgid(pid_t, pid_t);
+pid_t setpgrp(void);
+int setregid(gid_t, gid_t);
+int setreuid(uid_t, uid_t);
+pid_t setsid(void);
+int setuid(uid_t);
+void setusershell(void);
+unsigned int sleep(unsigned int);
+void swab(const void *__restrict, void *__restrict, ssize_t);
+int symlink(const char *, const char *);
+int symlinkat(const char *, int, const char *);
+void sync(void);
+long sysconf(int);
+pid_t tcgetpgrp(int);
+int tcsetpgrp(int, pid_t);
+int truncate(const char *, off_t);
+char *ttyname(int);
+int ttyname_r(int, char *, size_t);
+int unlink(const char *);
+int unlinkat(int, const char *, int);
+ssize_t write(int fd, const void *buffer, size_t size);
+
+extern char **environ;
+extern char *optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+// Non-POSIX functions supported by Linux.
+#if UINTPTR_MAX == UINT64_MAX
+typedef __mlibc_uint64 useconds_t;
+#else
+typedef __mlibc_uint32 useconds_t;
+#endif
+
+#ifndef __MLIBC_ABI_ONLY
+
+int getpagesize(void);
+char *get_current_dir_name(void);
+int usleep(useconds_t);
+int chroot(const char *);
+int daemon(int, int);
+
+// This is a Linux extension
+pid_t gettid(void);
+int getentropy(void *, size_t);
+
+int pipe2(int *pipefd, int flags);
+
+int setresuid(uid_t ruid, uid_t euid, uid_t suid);
+int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
+
+/* Glibc extensions. */
+int getdomainname(char *name, size_t len);
+int setdomainname(const char *name, size_t len);
+
+int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
+int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
+
+// Glibc doesn't provide them by default anymore, lock behind an option
+#if __MLIBC_CRYPT_OPTION
+char *crypt(const char *, const char *);
+void encrypt(char block[64], int flags);
+#endif
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#if __MLIBC_LINUX_OPTION
+# include <bits/linux/linux_unistd.h>
+#endif
+
+#if __MLIBC_BSD_OPTION
+# include <bits/bsd/bsd_unistd.h>
+#endif
+
+#endif // _UNISTD_H
+
diff --git a/lib/mlibc/options/posix/include/utime.h b/lib/mlibc/options/posix/include/utime.h
new file mode 100644
index 0000000..dcf053d
--- /dev/null
+++ b/lib/mlibc/options/posix/include/utime.h
@@ -0,0 +1,25 @@
+#ifndef _UTIME_H
+#define _UTIME_H
+
+#include <bits/ansi/time_t.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct utimbuf {
+ time_t actime;
+ time_t modtime;
+};
+
+#ifndef __MLIBC_ABI_ONLY
+
+int utime(const char *, const struct utimbuf *);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _UTIME_H
diff --git a/lib/mlibc/options/posix/include/wordexp.h b/lib/mlibc/options/posix/include/wordexp.h
new file mode 100644
index 0000000..e5d69ce
--- /dev/null
+++ b/lib/mlibc/options/posix/include/wordexp.h
@@ -0,0 +1,43 @@
+#ifndef _WORDEXP_H
+#define _WORDEXP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bits/size_t.h>
+
+#define WRDE_APPEND 1
+#define WRDE_DOOFFS 2
+#define WRDE_NOCMD 4
+#define WRDE_REUSE 8
+#define WRDE_SHOWERR 16
+#define WRDE_UNDEF 32
+
+#define WRDE_SUCCESS 1
+#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;
+ char *we_strings;
+ size_t we_nbytes;
+} wordexp_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+int wordexp(const char *s, wordexp_t *p, int flags);
+void wordfree(wordexp_t *p);
+
+#endif /* !__MLIBC_ABI_ONLY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/mlibc/options/posix/meson.build b/lib/mlibc/options/posix/meson.build
new file mode 100644
index 0000000..038dd2c
--- /dev/null
+++ b/lib/mlibc/options/posix/meson.build
@@ -0,0 +1,175 @@
+
+if disable_posix_option
+ subdir_done()
+endif
+libc_sources += files(
+ 'generic/arpa-inet-stubs.cpp',
+ 'generic/dirent-stubs.cpp',
+ 'generic/dlfcn-stubs.cpp',
+ 'generic/fcntl-stubs.cpp',
+ 'generic/ftw-stubs.cpp',
+ 'generic/grp-stubs.cpp',
+ 'generic/langinfo-stubs.cpp',
+ 'generic/libgen-stubs.cpp',
+ 'generic/lookup.cpp',
+ 'generic/netdb-stubs.cpp',
+ 'generic/net-if-stubs.cpp',
+ 'generic/poll.cpp',
+ 'generic/posix_ctype.cpp',
+ 'generic/posix-file-io.cpp',
+ 'generic/posix_locale.cpp',
+ 'generic/posix_signal.cpp',
+ 'generic/posix_stdio.cpp',
+ 'generic/posix_stdlib.cpp',
+ 'generic/posix_string.cpp',
+ 'generic/posix_time.cpp',
+ 'generic/pthread-stubs.cpp',
+ 'generic/pwd-stubs.cpp',
+ 'generic/resolv_conf.cpp',
+ 'generic/sched-stubs.cpp',
+ 'generic/spawn-stubs.cpp',
+ 'generic/strings-stubs.cpp',
+ 'generic/services.cpp',
+ 'generic/sys-file-stubs.cpp',
+ 'generic/syslog-stubs.cpp',
+ 'generic/sys-mman-stubs.cpp',
+ 'generic/sys-resource-stubs.cpp',
+ 'generic/sys-select-stubs.cpp',
+ 'generic/sys-shm.cpp',
+ 'generic/sys-socket-stubs.cpp',
+ 'generic/sys-stat-stubs.cpp',
+ 'generic/sys-statvfs-stubs.cpp',
+ 'generic/sys-times.cpp',
+ 'generic/sys-time-stubs.cpp',
+ 'generic/sys-uio.cpp',
+ 'generic/sys-utsname.cpp',
+ 'generic/sys-wait-stubs.cpp',
+ 'generic/termios-stubs.cpp',
+ 'generic/unistd-stubs.cpp',
+ 'generic/utime-stubs.cpp',
+ 'generic/ucontext-stubs.cpp',
+ 'generic/semaphore-stubs.cpp',
+ 'generic/search.cpp',
+ 'generic/sys-msg.cpp',
+ 'generic/sys-sem.cpp',
+ 'generic/sys-ipc.cpp',
+ 'generic/time.cpp',
+ 'generic/wordexp-stubs.cpp',
+ 'generic/mqueue.cpp'
+)
+
+if not headers_only
+ libc_sublibs += static_library('musl-generic-regex',
+ 'musl-generic-regex/fnmatch.c',
+ 'musl-generic-regex/glob.c',
+ 'musl-generic-regex/regcomp.c',
+ 'musl-generic-regex/regerror.c',
+ 'musl-generic-regex/regexec.c',
+ 'musl-generic-regex/tre-mem.c',
+ pic: true,
+ include_directories: libc_include_dirs,
+ dependencies: libc_deps,
+ c_args: ['-Wno-unused', '-Wno-implicit', '-Wno-parentheses', '-Wno-sign-compare', '-Wno-attributes', '-Wno-unknown-pragmas', '-Wno-implicit-fallthrough']
+ )
+endif
+
+if not no_headers
+ install_headers(
+ 'include/byteswap.h',
+ 'include/dirent.h',
+ 'include/dlfcn.h',
+ 'include/fcntl.h',
+ 'include/fnmatch.h',
+ 'include/ftw.h',
+ 'include/glob.h',
+ 'include/grp.h',
+ 'include/langinfo.h',
+ 'include/libgen.h',
+ 'include/netdb.h',
+ 'include/nl_types.h',
+ 'include/pthread.h',
+ 'include/pwd.h',
+ 'include/poll.h',
+ 'include/regex.h',
+ 'include/sched.h',
+ 'include/search.h',
+ 'include/spawn.h',
+ 'include/strings.h',
+ 'include/syslog.h',
+ 'include/termios.h',
+ 'include/unistd.h',
+ 'include/utime.h',
+ 'include/ucontext.h',
+ 'include/wordexp.h',
+ 'include/semaphore.h',
+ 'include/mqueue.h',
+ )
+ install_headers(
+ 'include/arpa/inet.h',
+ subdir: 'arpa'
+ )
+ install_headers(
+ 'include/net/if.h',
+ 'include/net/if_arp.h',
+ subdir: 'net'
+ )
+ install_headers(
+ 'include/netinet/in.h',
+ 'include/netinet/ip.h',
+ 'include/netinet/tcp.h',
+ 'include/netinet/icmp6.h',
+ 'include/netinet/if_ether.h',
+ 'include/netinet/udp.h',
+ 'include/netinet/ip6.h',
+ 'include/netinet/ip_icmp.h',
+ subdir: 'netinet'
+ )
+ install_headers(
+ 'include/sys/file.h',
+ 'include/sys/ipc.h',
+ 'include/sys/mman.h',
+ 'include/sys/msg.h',
+ 'include/sys/param.h',
+ 'include/sys/poll.h',
+ 'include/sys/resource.h',
+ 'include/sys/select.h',
+ 'include/sys/sem.h',
+ 'include/sys/shm.h',
+ 'include/sys/socket.h',
+ 'include/sys/stat.h',
+ 'include/sys/statvfs.h',
+ 'include/sys/termios.h',
+ 'include/sys/time.h',
+ 'include/sys/times.h',
+ 'include/sys/ttydefaults.h',
+ 'include/sys/types.h',
+ 'include/sys/uio.h',
+ 'include/sys/un.h',
+ 'include/sys/utsname.h',
+ 'include/sys/wait.h',
+ 'include/sys/syslog.h',
+ subdir: 'sys'
+ )
+ install_headers(
+ 'include/bits/posix/id_t.h',
+ 'include/bits/posix/in_addr_t.h',
+ 'include/bits/posix/in_port_t.h',
+ 'include/bits/posix/iovec.h',
+ 'include/bits/posix/locale_t.h',
+ 'include/bits/posix/posix_ctype.h',
+ 'include/bits/posix/posix_locale.h',
+ 'include/bits/posix/posix_signal.h',
+ 'include/bits/posix/posix_stdio.h',
+ 'include/bits/posix/posix_stdlib.h',
+ 'include/bits/posix/posix_string.h',
+ 'include/bits/posix/posix_time.h',
+ 'include/bits/posix/posix_wctype.h',
+ 'include/bits/posix/stat.h',
+ 'include/bits/posix/timeval.h',
+ 'include/bits/posix/fd_set.h',
+ 'include/bits/posix/pthread_t.h',
+ 'include/bits/posix/timer_t.h',
+ subdir: 'bits/posix'
+ )
+endif
+
diff --git a/lib/mlibc/options/posix/musl-generic-regex/fnmatch.c b/lib/mlibc/options/posix/musl-generic-regex/fnmatch.c
new file mode 100644
index 0000000..0e6de47
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/fnmatch.c
@@ -0,0 +1,321 @@
+/*
+ * An implementation of what I call the "Sea of Stars" algorithm for
+ * POSIX fnmatch(). The basic idea is that we factor the pattern into
+ * a head component (which we match first and can reject without ever
+ * measuring the length of the string), an optional tail component
+ * (which only exists if the pattern contains at least one star), and
+ * an optional "sea of stars", a set of star-separated components
+ * between the head and tail. After the head and tail matches have
+ * been removed from the input string, the components in the "sea of
+ * stars" are matched sequentially by searching for their first
+ * occurrence past the end of the previous match.
+ *
+ * - Rich Felker, April 2012
+ */
+
+#include <string.h>
+#include <fnmatch.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+// #include "locale_impl.h"
+
+#define END 0
+#define UNMATCHABLE -2
+#define BRACKET -3
+#define QUESTION -4
+#define STAR -5
+
+static int str_next(const char *str, size_t n, size_t *step)
+{
+ if (!n) {
+ *step = 0;
+ return 0;
+ }
+ if (str[0] >= 128U) {
+ wchar_t wc;
+ int k = mbtowc(&wc, str, n);
+ if (k<0) {
+ *step = 1;
+ return -1;
+ }
+ *step = k;
+ return wc;
+ }
+ *step = 1;
+ return str[0];
+}
+
+static int pat_next(const char *pat, size_t m, size_t *step, int flags)
+{
+ int esc = 0;
+ if (!m || !*pat) {
+ *step = 0;
+ return END;
+ }
+ *step = 1;
+ if (pat[0]=='\\' && pat[1] && !(flags & FNM_NOESCAPE)) {
+ *step = 2;
+ pat++;
+ esc = 1;
+ goto escaped;
+ }
+ if (pat[0]=='[') {
+ size_t k = 1;
+ if (k<m) if (pat[k] == '^' || pat[k] == '!') k++;
+ if (k<m) if (pat[k] == ']') k++;
+ for (; k<m && pat[k] && pat[k]!=']'; k++) {
+ if (k+1<m && pat[k+1] && pat[k]=='[' && (pat[k+1]==':' || pat[k+1]=='.' || pat[k+1]=='=')) {
+ int z = pat[k+1];
+ k+=2;
+ if (k<m && pat[k]) k++;
+ while (k<m && pat[k] && (pat[k-1]!=z || pat[k]!=']')) k++;
+ if (k==m || !pat[k]) break;
+ }
+ }
+ if (k==m || !pat[k]) {
+ *step = 1;
+ return '[';
+ }
+ *step = k+1;
+ return BRACKET;
+ }
+ if (pat[0] == '*')
+ return STAR;
+ if (pat[0] == '?')
+ return QUESTION;
+escaped:
+ if (pat[0] >= 128U) {
+ wchar_t wc;
+ int k = mbtowc(&wc, pat, m);
+ if (k<0) {
+ *step = 0;
+ return UNMATCHABLE;
+ }
+ *step = k + esc;
+ return wc;
+ }
+ return pat[0];
+}
+
+static int casefold(int k)
+{
+ int c = towupper(k);
+ return c == k ? towlower(k) : c;
+}
+
+static int match_bracket(const char *p, int k, int kfold)
+{
+ wchar_t wc;
+ int inv = 0;
+ p++;
+ if (*p=='^' || *p=='!') {
+ inv = 1;
+ p++;
+ }
+ if (*p==']') {
+ if (k==']') return !inv;
+ p++;
+ } else if (*p=='-') {
+ if (k=='-') return !inv;
+ p++;
+ }
+ wc = p[-1];
+ for (; *p != ']'; p++) {
+ if (p[0]=='-' && p[1]!=']') {
+ wchar_t wc2;
+ int l = mbtowc(&wc2, p+1, 4);
+ if (l < 0) return 0;
+ if (wc <= wc2)
+ if ((unsigned)k-wc <= wc2-wc ||
+ (unsigned)kfold-wc <= wc2-wc)
+ return !inv;
+ p += l-1;
+ continue;
+ }
+ if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) {
+ const char *p0 = p+2;
+ int z = p[1];
+ p+=3;
+ while (p[-1]!=z || p[0]!=']') p++;
+ if (z == ':' && p-1-p0 < 16) {
+ char buf[16];
+ memcpy(buf, p0, p-1-p0);
+ buf[p-1-p0] = 0;
+ if (iswctype(k, wctype(buf)) ||
+ iswctype(kfold, wctype(buf)))
+ return !inv;
+ }
+ continue;
+ }
+ if (*p < 128U) {
+ wc = (unsigned char)*p;
+ } else {
+ int l = mbtowc(&wc, p, 4);
+ if (l < 0) return 0;
+ p += l-1;
+ }
+ if (wc==(wchar_t)k || wc==(wchar_t)kfold) return !inv;
+ }
+ return inv;
+}
+
+static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n, int flags)
+{
+ const char *p, *ptail, *endpat;
+ const char *s, *stail, *endstr;
+ size_t pinc, sinc, tailcnt=0;
+ int c, k, kfold;
+
+ if (flags & FNM_PERIOD) {
+ if (*str == '.' && *pat != '.')
+ return FNM_NOMATCH;
+ }
+ for (;;) {
+ switch ((c = pat_next(pat, m, &pinc, flags))) {
+ case UNMATCHABLE:
+ return FNM_NOMATCH;
+ case STAR:
+ pat++;
+ m--;
+ break;
+ default:
+ k = str_next(str, n, &sinc);
+ if (k <= 0)
+ return (c==END) ? 0 : FNM_NOMATCH;
+ str += sinc;
+ n -= sinc;
+ kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
+ if (c == BRACKET) {
+ if (!match_bracket(pat, k, kfold))
+ return FNM_NOMATCH;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ return FNM_NOMATCH;
+ }
+ pat+=pinc;
+ m-=pinc;
+ continue;
+ }
+ break;
+ }
+
+ /* Compute real pat length if it was initially unknown/-1 */
+ m = strnlen(pat, m);
+ endpat = pat + m;
+
+ /* Find the last * in pat and count chars needed after it */
+ for (p=ptail=pat; p<endpat; p+=pinc) {
+ switch (pat_next(p, endpat-p, &pinc, flags)) {
+ case UNMATCHABLE:
+ return FNM_NOMATCH;
+ case STAR:
+ tailcnt=0;
+ ptail = p+1;
+ break;
+ default:
+ tailcnt++;
+ break;
+ }
+ }
+
+ /* Past this point we need not check for UNMATCHABLE in pat,
+ * because all of pat has already been parsed once. */
+
+ /* Compute real str length if it was initially unknown/-1 */
+ n = strnlen(str, n);
+ endstr = str + n;
+ if (n < tailcnt) return FNM_NOMATCH;
+
+ /* Find the final tailcnt chars of str, accounting for UTF-8.
+ * On illegal sequences we may get it wrong, but in that case
+ * we necessarily have a matching failure anyway. */
+ for (s=endstr; s>str && tailcnt; tailcnt--) {
+ if (s[-1] < 128U || MB_CUR_MAX==1) s--;
+ else while ((unsigned char)*--s-0x80U<0x40 && s>str);
+ }
+ if (tailcnt) return FNM_NOMATCH;
+ stail = s;
+
+ /* Check that the pat and str tails match */
+ p = ptail;
+ for (;;) {
+ c = pat_next(p, endpat-p, &pinc, flags);
+ p += pinc;
+ if ((k = str_next(s, endstr-s, &sinc)) <= 0) {
+ if (c != END) return FNM_NOMATCH;
+ break;
+ }
+ s += sinc;
+ kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
+ if (c == BRACKET) {
+ if (!match_bracket(p-pinc, k, kfold))
+ return FNM_NOMATCH;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ return FNM_NOMATCH;
+ }
+ }
+
+ /* We're all done with the tails now, so throw them out */
+ endstr = stail;
+ endpat = ptail;
+
+ /* Match pattern components until there are none left */
+ while (pat<endpat) {
+ p = pat;
+ s = str;
+ for (;;) {
+ c = pat_next(p, endpat-p, &pinc, flags);
+ p += pinc;
+ /* Encountering * completes/commits a component */
+ if (c == STAR) {
+ pat = p;
+ str = s;
+ break;
+ }
+ k = str_next(s, endstr-s, &sinc);
+ if (!k)
+ return FNM_NOMATCH;
+ kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
+ if (c == BRACKET) {
+ if (!match_bracket(p-pinc, k, kfold))
+ break;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ break;
+ }
+ s += sinc;
+ }
+ if (c == STAR) continue;
+ /* If we failed, advance str, by 1 char if it's a valid
+ * char, or past all invalid bytes otherwise. */
+ k = str_next(str, endstr-str, &sinc);
+ if (k > 0) str += sinc;
+ else for (str++; str_next(str, endstr-str, &sinc)<0; str++);
+ }
+
+ return 0;
+}
+
+int fnmatch(const char *pat, const char *str, int flags)
+{
+ const char *s, *p;
+ size_t inc;
+ int c;
+ if (flags & FNM_PATHNAME) for (;;) {
+ for (s=str; *s && *s!='/'; s++);
+ for (p=pat; (c=pat_next(p, -1, &inc, flags))!=END && c!='/'; p+=inc);
+ if (c!=*s && (!*s || !(flags & FNM_LEADING_DIR)))
+ return FNM_NOMATCH;
+ if (fnmatch_internal(pat, p-pat, str, s-str, flags))
+ return FNM_NOMATCH;
+ if (!c) return 0;
+ str = s+1;
+ pat = p+inc;
+ } else if (flags & FNM_LEADING_DIR) {
+ for (s=str; *s; s++) {
+ if (*s != '/') continue;
+ if (!fnmatch_internal(pat, -1, str, s-str, flags))
+ return 0;
+ }
+ }
+ return fnmatch_internal(pat, -1, str, -1, flags);
+}
diff --git a/lib/mlibc/options/posix/musl-generic-regex/glob.c b/lib/mlibc/options/posix/musl-generic-regex/glob.c
new file mode 100644
index 0000000..b57f2f3
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/glob.c
@@ -0,0 +1,311 @@
+#define _BSD_SOURCE
+#include <glob.h>
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <pwd.h>
+
+struct match
+{
+ struct match *next;
+ char name[];
+};
+
+static int append(struct match **tail, const char *name, size_t len, int mark)
+{
+ struct match *new = malloc(sizeof(struct match) + len + 2);
+ if (!new) return -1;
+ (*tail)->next = new;
+ new->next = NULL;
+ memcpy(new->name, name, len+1);
+ if (mark && len && name[len-1]!='/') {
+ new->name[len] = '/';
+ new->name[len+1] = 0;
+ }
+ *tail = new;
+ return 0;
+}
+
+static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (*errfunc)(const char *path, int err), struct match **tail)
+{
+ /* If GLOB_MARK is unused, we don't care about type. */
+ if (!type && !(flags & GLOB_MARK)) type = DT_REG;
+
+ /* Special-case the remaining pattern being all slashes, in
+ * which case we can use caller-passed type if it's a dir. */
+ if (*pat && type!=DT_DIR) type = 0;
+ while (pos+1 < PATH_MAX && *pat=='/') buf[pos++] = *pat++;
+
+ /* Consume maximal [escaped-]literal prefix of pattern, copying
+ * and un-escaping it to the running buffer as we go. */
+ ptrdiff_t i=0, j=0;
+ int in_bracket = 0, overflow = 0;
+ for (; pat[i]!='*' && pat[i]!='?' && (!in_bracket || pat[i]!=']'); i++) {
+ if (!pat[i]) {
+ if (overflow) return 0;
+ pat += i;
+ pos += j;
+ i = j = 0;
+ break;
+ } else if (pat[i] == '[') {
+ in_bracket = 1;
+ } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) {
+ /* Backslashes inside a bracket are (at least by
+ * our interpretation) non-special, so if next
+ * char is ']' we have a complete expression. */
+ if (in_bracket && pat[i+1]==']') break;
+ /* Unpaired final backslash never matches. */
+ if (!pat[i+1]) return 0;
+ i++;
+ }
+ if (pat[i] == '/') {
+ if (overflow) return 0;
+ in_bracket = 0;
+ pat += i+1;
+ i = -1;
+ pos += j+1;
+ j = -1;
+ }
+ /* Only store a character if it fits in the buffer, but if
+ * a potential bracket expression is open, the overflow
+ * must be remembered and handled later only if the bracket
+ * is unterminated (and thereby a literal), so as not to
+ * disallow long bracket expressions with short matches. */
+ if (pos+(j+1) < PATH_MAX) {
+ buf[pos+j++] = pat[i];
+ } else if (in_bracket) {
+ overflow = 1;
+ } else {
+ return 0;
+ }
+ /* If we consume any new components, the caller-passed type
+ * or dummy type from above is no longer valid. */
+ type = 0;
+ }
+ buf[pos] = 0;
+ if (!*pat) {
+ /* If we consumed any components above, or if GLOB_MARK is
+ * requested and we don't yet know if the match is a dir,
+ * we must confirm the file exists and/or determine its type.
+ *
+ * If marking dirs, symlink type is inconclusive; we need the
+ * type for the symlink target, and therefore must try stat
+ * first unless type is known not to be a symlink. Otherwise,
+ * or if that fails, use lstat for determining existence to
+ * avoid false negatives in the case of broken symlinks. */
+ struct stat st;
+ if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) {
+ if (S_ISDIR(st.st_mode)) type = DT_DIR;
+ else type = DT_REG;
+ }
+ if (!type && lstat(buf, &st)) {
+ if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR)))
+ return GLOB_ABORTED;
+ return 0;
+ }
+ if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR))
+ return GLOB_NOSPACE;
+ return 0;
+ }
+ char *p2 = strchr(pat, '/'), saved_sep = '/';
+ /* Check if the '/' was escaped and, if so, remove the escape char
+ * so that it will not be unpaired when passed to fnmatch. */
+ if (p2 && !(flags & GLOB_NOESCAPE)) {
+ char *p;
+ for (p=p2; p>pat && p[-1]=='\\'; p--);
+ if ((p2-p)%2) {
+ p2--;
+ saved_sep = '\\';
+ }
+ }
+ DIR *dir = opendir(pos ? buf : ".");
+ if (!dir) {
+ if (errfunc(buf, errno) || (flags & GLOB_ERR))
+ return GLOB_ABORTED;
+ return 0;
+ }
+ int old_errno = errno;
+ struct dirent *de;
+ while (errno=0, de=readdir(dir)) {
+ /* Quickly skip non-directories when there's pattern left. */
+ if (p2 && de->d_type && de->d_type!=DT_DIR && de->d_type!=DT_LNK)
+ continue;
+
+ size_t l = strlen(de->d_name);
+ if (l >= PATH_MAX-pos) continue;
+
+ if (p2) *p2 = 0;
+
+ int fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
+ | ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0);
+
+ if (fnmatch(pat, de->d_name, fnm_flags))
+ continue;
+
+ /* With GLOB_PERIOD, don't allow matching . or .. unless
+ * fnmatch would match them with FNM_PERIOD rules in effect. */
+ if (p2 && (flags & GLOB_PERIOD) && de->d_name[0]=='.'
+ && (!de->d_name[1] || de->d_name[1]=='.' && !de->d_name[2])
+ && fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD))
+ continue;
+
+ memcpy(buf+pos, de->d_name, l+1);
+ if (p2) *p2 = saved_sep;
+ int r = do_glob(buf, pos+l, de->d_type, p2 ? p2 : "", flags, errfunc, tail);
+ if (r) {
+ closedir(dir);
+ return r;
+ }
+ }
+ int readerr = errno;
+ if (p2) *p2 = saved_sep;
+ closedir(dir);
+ if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR)))
+ return GLOB_ABORTED;
+ errno = old_errno;
+ return 0;
+}
+
+static int ignore_err(const char *path, int err)
+{
+ return 0;
+}
+
+static void freelist(struct match *head)
+{
+ struct match *match, *next;
+ for (match=head->next; match; match=next) {
+ next = match->next;
+ free(match);
+ }
+}
+
+static int sort(const void *a, const void *b)
+{
+ return strcmp(*(const char **)a, *(const char **)b);
+}
+
+static int expand_tilde(char **pat, char *buf, size_t *pos)
+{
+ char *p = *pat + 1;
+ size_t i = 0;
+
+ char delim, *name_end = strchrnul(p, '/');
+ if ((delim = *name_end)) *name_end++ = 0;
+ *pat = name_end;
+
+ char *home = *p ? NULL : getenv("HOME");
+ if (!home) {
+ struct passwd pw, *res;
+ switch (*p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res)
+ : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res)) {
+ case ENOMEM:
+ return GLOB_NOSPACE;
+ case 0:
+ if (!res)
+ default:
+ return GLOB_NOMATCH;
+ }
+ home = pw.pw_dir;
+ }
+ while (i < PATH_MAX - 2 && *home)
+ buf[i++] = *home++;
+ if (*home)
+ return GLOB_NOMATCH;
+ if ((buf[i] = delim))
+ buf[++i] = 0;
+ *pos = i;
+ return 0;
+}
+
+int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g)
+{
+ struct match head = { .next = NULL }, *tail = &head;
+ size_t cnt, i;
+ size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0;
+ int error = 0;
+ char buf[PATH_MAX];
+
+ if (!errfunc) errfunc = ignore_err;
+
+ if (!(flags & GLOB_APPEND)) {
+ g->gl_offs = offs;
+ g->gl_pathc = 0;
+ g->gl_pathv = NULL;
+ }
+
+ if (*pat) {
+ char *p = strdup(pat);
+ if (!p) return GLOB_NOSPACE;
+ buf[0] = 0;
+ size_t pos = 0;
+ char *s = p;
+ if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~')
+ error = expand_tilde(&s, buf, &pos);
+ if (!error)
+ error = do_glob(buf, pos, 0, s, flags, errfunc, &tail);
+ free(p);
+ }
+
+ if (error == GLOB_NOSPACE) {
+ freelist(&head);
+ return error;
+ }
+
+ for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++);
+ if (!cnt) {
+ if (flags & GLOB_NOCHECK) {
+ tail = &head;
+ if (append(&tail, pat, strlen(pat), 0))
+ return GLOB_NOSPACE;
+ cnt++;
+ } else
+ return GLOB_NOMATCH;
+ }
+
+ if (flags & GLOB_APPEND) {
+ char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *));
+ if (!pathv) {
+ freelist(&head);
+ return GLOB_NOSPACE;
+ }
+ g->gl_pathv = pathv;
+ offs += g->gl_pathc;
+ } else {
+ g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *));
+ if (!g->gl_pathv) {
+ freelist(&head);
+ return GLOB_NOSPACE;
+ }
+ for (i=0; i<offs; i++)
+ g->gl_pathv[i] = NULL;
+ }
+ for (i=0, tail=head.next; i<cnt; tail=tail->next, i++)
+ g->gl_pathv[offs + i] = tail->name;
+ g->gl_pathv[offs + i] = NULL;
+ g->gl_pathc += cnt;
+
+ if (!(flags & GLOB_NOSORT))
+ qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort);
+
+ return error;
+}
+
+void globfree(glob_t *g)
+{
+ size_t i;
+ for (i=0; i<g->gl_pathc; i++)
+ free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name));
+ free(g->gl_pathv);
+ g->gl_pathc = 0;
+ g->gl_pathv = NULL;
+}
+
+// weak_alias(glob, glob64);
+// weak_alias(globfree, globfree64);
diff --git a/lib/mlibc/options/posix/musl-generic-regex/regcomp.c b/lib/mlibc/options/posix/musl-generic-regex/regcomp.c
new file mode 100644
index 0000000..ab03984
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/regcomp.c
@@ -0,0 +1,2953 @@
+/*
+ regcomp.c - TRE POSIX compatible regex compilation functions.
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <limits.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include "tre.h"
+
+#include <assert.h>
+
+/***********************************************************************
+ from tre-compile.h
+***********************************************************************/
+
+typedef struct {
+ int position;
+ int code_min;
+ int code_max;
+ int *tags;
+ int assertions;
+ tre_ctype_t class;
+ tre_ctype_t *neg_classes;
+ int backref;
+} tre_pos_and_tags_t;
+
+
+/***********************************************************************
+ from tre-ast.c and tre-ast.h
+***********************************************************************/
+
+/* The different AST node types. */
+typedef enum {
+ LITERAL,
+ CATENATION,
+ ITERATION,
+ UNION
+} tre_ast_type_t;
+
+/* Special subtypes of TRE_LITERAL. */
+#define EMPTY -1 /* Empty leaf (denotes empty string). */
+#define ASSERTION -2 /* Assertion leaf. */
+#define TAG -3 /* Tag leaf. */
+#define BACKREF -4 /* Back reference leaf. */
+
+#define IS_SPECIAL(x) ((x)->code_min < 0)
+#define IS_EMPTY(x) ((x)->code_min == EMPTY)
+#define IS_ASSERTION(x) ((x)->code_min == ASSERTION)
+#define IS_TAG(x) ((x)->code_min == TAG)
+#define IS_BACKREF(x) ((x)->code_min == BACKREF)
+
+
+/* A generic AST node. All AST nodes consist of this node on the top
+ level with `obj' pointing to the actual content. */
+typedef struct {
+ tre_ast_type_t type; /* Type of the node. */
+ void *obj; /* Pointer to actual node. */
+ int nullable;
+ int submatch_id;
+ int num_submatches;
+ int num_tags;
+ tre_pos_and_tags_t *firstpos;
+ tre_pos_and_tags_t *lastpos;
+} tre_ast_node_t;
+
+
+/* A "literal" node. These are created for assertions, back references,
+ tags, matching parameter settings, and all expressions that match one
+ character. */
+typedef struct {
+ long code_min;
+ long code_max;
+ int position;
+ tre_ctype_t class;
+ tre_ctype_t *neg_classes;
+} tre_literal_t;
+
+/* A "catenation" node. These are created when two regexps are concatenated.
+ If there are more than one subexpressions in sequence, the `left' part
+ holds all but the last, and `right' part holds the last subexpression
+ (catenation is left associative). */
+typedef struct {
+ tre_ast_node_t *left;
+ tre_ast_node_t *right;
+} tre_catenation_t;
+
+/* An "iteration" node. These are created for the "*", "+", "?", and "{m,n}"
+ operators. */
+typedef struct {
+ /* Subexpression to match. */
+ tre_ast_node_t *arg;
+ /* Minimum number of consecutive matches. */
+ int min;
+ /* Maximum number of consecutive matches. */
+ int max;
+ /* If 0, match as many characters as possible, if 1 match as few as
+ possible. Note that this does not always mean the same thing as
+ matching as many/few repetitions as possible. */
+ unsigned int minimal:1;
+} tre_iteration_t;
+
+/* An "union" node. These are created for the "|" operator. */
+typedef struct {
+ tre_ast_node_t *left;
+ tre_ast_node_t *right;
+} tre_union_t;
+
+
+static tre_ast_node_t *
+tre_ast_new_node(tre_mem_t mem, int type, void *obj)
+{
+ tre_ast_node_t *node = tre_mem_calloc(mem, sizeof *node);
+ if (!node || !obj)
+ return 0;
+ node->obj = obj;
+ node->type = type;
+ node->nullable = -1;
+ node->submatch_id = -1;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position)
+{
+ tre_ast_node_t *node;
+ tre_literal_t *lit;
+
+ lit = tre_mem_calloc(mem, sizeof *lit);
+ node = tre_ast_new_node(mem, LITERAL, lit);
+ if (!node)
+ return 0;
+ lit->code_min = code_min;
+ lit->code_max = code_max;
+ lit->position = position;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, int minimal)
+{
+ tre_ast_node_t *node;
+ tre_iteration_t *iter;
+
+ iter = tre_mem_calloc(mem, sizeof *iter);
+ node = tre_ast_new_node(mem, ITERATION, iter);
+ if (!node)
+ return 0;
+ iter->arg = arg;
+ iter->min = min;
+ iter->max = max;
+ iter->minimal = minimal;
+ node->num_submatches = arg->num_submatches;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
+{
+ tre_ast_node_t *node;
+ tre_union_t *un;
+
+ if (!left)
+ return right;
+ un = tre_mem_calloc(mem, sizeof *un);
+ node = tre_ast_new_node(mem, UNION, un);
+ if (!node || !right)
+ return 0;
+ un->left = left;
+ un->right = right;
+ node->num_submatches = left->num_submatches + right->num_submatches;
+ return node;
+}
+
+static tre_ast_node_t *
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
+{
+ tre_ast_node_t *node;
+ tre_catenation_t *cat;
+
+ if (!left)
+ return right;
+ cat = tre_mem_calloc(mem, sizeof *cat);
+ node = tre_ast_new_node(mem, CATENATION, cat);
+ if (!node)
+ return 0;
+ cat->left = left;
+ cat->right = right;
+ node->num_submatches = left->num_submatches + right->num_submatches;
+ return node;
+}
+
+
+/***********************************************************************
+ from tre-stack.c and tre-stack.h
+***********************************************************************/
+
+typedef struct tre_stack_rec tre_stack_t;
+
+/* Creates a new stack object. `size' is initial size in bytes, `max_size'
+ is maximum size, and `increment' specifies how much more space will be
+ allocated with realloc() if all space gets used up. Returns the stack
+ object or NULL if out of memory. */
+static tre_stack_t *
+tre_stack_new(int size, int max_size, int increment);
+
+/* Frees the stack object. */
+static void
+tre_stack_destroy(tre_stack_t *s);
+
+/* Returns the current number of objects in the stack. */
+static int
+tre_stack_num_objects(tre_stack_t *s);
+
+/* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes
+ `value' on top of stack `s'. Returns REG_ESPACE if out of memory.
+ This tries to realloc() more space before failing if maximum size
+ has not yet been reached. Returns REG_OK if successful. */
+#define declare_pushf(typetag, type) \
+ static reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value)
+
+declare_pushf(voidptr, void *);
+declare_pushf(int, int);
+
+/* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost
+ element off of stack `s' and returns it. The stack must not be
+ empty. */
+#define declare_popf(typetag, type) \
+ static type tre_stack_pop_ ## typetag(tre_stack_t *s)
+
+declare_popf(voidptr, void *);
+declare_popf(int, int);
+
+/* Just to save some typing. */
+#define STACK_PUSH(s, typetag, value) \
+ do \
+ { \
+ status = tre_stack_push_ ## typetag(s, value); \
+ } \
+ while (/*CONSTCOND*/0)
+
+#define STACK_PUSHX(s, typetag, value) \
+ { \
+ status = tre_stack_push_ ## typetag(s, value); \
+ if (status != REG_OK) \
+ break; \
+ }
+
+#define STACK_PUSHR(s, typetag, value) \
+ { \
+ reg_errcode_t _status; \
+ _status = tre_stack_push_ ## typetag(s, value); \
+ if (_status != REG_OK) \
+ return _status; \
+ }
+
+union tre_stack_item {
+ void *voidptr_value;
+ int int_value;
+};
+
+struct tre_stack_rec {
+ int size;
+ int max_size;
+ int increment;
+ int ptr;
+ union tre_stack_item *stack;
+};
+
+
+static tre_stack_t *
+tre_stack_new(int size, int max_size, int increment)
+{
+ tre_stack_t *s;
+
+ s = xmalloc(sizeof(*s));
+ if (s != NULL)
+ {
+ s->stack = xmalloc(sizeof(*s->stack) * size);
+ if (s->stack == NULL)
+ {
+ xfree(s);
+ return NULL;
+ }
+ s->size = size;
+ s->max_size = max_size;
+ s->increment = increment;
+ s->ptr = 0;
+ }
+ return s;
+}
+
+static void
+tre_stack_destroy(tre_stack_t *s)
+{
+ xfree(s->stack);
+ xfree(s);
+}
+
+static int
+tre_stack_num_objects(tre_stack_t *s)
+{
+ return s->ptr;
+}
+
+static reg_errcode_t
+tre_stack_push(tre_stack_t *s, union tre_stack_item value)
+{
+ if (s->ptr < s->size)
+ {
+ s->stack[s->ptr] = value;
+ s->ptr++;
+ }
+ else
+ {
+ if (s->size >= s->max_size)
+ {
+ return REG_ESPACE;
+ }
+ else
+ {
+ union tre_stack_item *new_buffer;
+ int new_size;
+ new_size = s->size + s->increment;
+ if (new_size > s->max_size)
+ new_size = s->max_size;
+ new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size);
+ if (new_buffer == NULL)
+ {
+ return REG_ESPACE;
+ }
+ assert(new_size > s->size);
+ s->size = new_size;
+ s->stack = new_buffer;
+ tre_stack_push(s, value);
+ }
+ }
+ return REG_OK;
+}
+
+#define define_pushf(typetag, type) \
+ declare_pushf(typetag, type) { \
+ union tre_stack_item item; \
+ item.typetag ## _value = value; \
+ return tre_stack_push(s, item); \
+}
+
+define_pushf(int, int)
+define_pushf(voidptr, void *)
+
+#define define_popf(typetag, type) \
+ declare_popf(typetag, type) { \
+ return s->stack[--s->ptr].typetag ## _value; \
+ }
+
+define_popf(int, int)
+define_popf(voidptr, void *)
+
+
+/***********************************************************************
+ from tre-parse.c and tre-parse.h
+***********************************************************************/
+
+/* Parse context. */
+typedef struct {
+ /* Memory allocator. The AST is allocated using this. */
+ tre_mem_t mem;
+ /* Stack used for keeping track of regexp syntax. */
+ tre_stack_t *stack;
+ /* The parsed node after a parse function returns. */
+ tre_ast_node_t *n;
+ /* Position in the regexp pattern after a parse function returns. */
+ const char *s;
+ /* The first character of the last subexpression parsed. */
+ const char *start;
+ /* Current submatch ID. */
+ int submatch_id;
+ /* Current position (number of literal). */
+ int position;
+ /* The highest back reference or -1 if none seen so far. */
+ int max_backref;
+ /* Compilation flags. */
+ int cflags;
+} tre_parse_ctx_t;
+
+/* Some macros for expanding \w, \s, etc. */
+static const struct {
+ char c;
+ const char *expansion;
+} tre_macros[] = {
+ {'t', "\t"}, {'n', "\n"}, {'r', "\r"},
+ {'f', "\f"}, {'a', "\a"}, {'e', "\033"},
+ {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
+ {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"},
+ { 0, 0 }
+};
+
+/* Expands a macro delimited by `regex' and `regex_end' to `buf', which
+ must have at least `len' items. Sets buf[0] to zero if the there
+ is no match in `tre_macros'. */
+static const char *tre_expand_macro(const char *s)
+{
+ int i;
+ for (i = 0; tre_macros[i].c && tre_macros[i].c != *s; i++);
+ return tre_macros[i].expansion;
+}
+
+static int
+tre_compare_lit(const void *a, const void *b)
+{
+ const tre_literal_t *const *la = a;
+ const tre_literal_t *const *lb = b;
+ /* assumes the range of valid code_min is < INT_MAX */
+ return la[0]->code_min - lb[0]->code_min;
+}
+
+struct literals {
+ tre_mem_t mem;
+ tre_literal_t **a;
+ int len;
+ int cap;
+};
+
+static tre_literal_t *tre_new_lit(struct literals *p)
+{
+ tre_literal_t **a;
+ if (p->len >= p->cap) {
+ if (p->cap >= 1<<15)
+ return 0;
+ p->cap *= 2;
+ a = xrealloc(p->a, p->cap * sizeof *p->a);
+ if (!a)
+ return 0;
+ p->a = a;
+ }
+ a = p->a + p->len++;
+ *a = tre_mem_calloc(p->mem, sizeof **a);
+ return *a;
+}
+
+static int add_icase_literals(struct literals *ls, int min, int max)
+{
+ tre_literal_t *lit;
+ int b, e, c;
+ for (c=min; c<=max; ) {
+ /* assumes islower(c) and isupper(c) are exclusive
+ and toupper(c)!=c if islower(c).
+ multiple opposite case characters are not supported */
+ if (tre_islower(c)) {
+ b = e = tre_toupper(c);
+ for (c++, e++; c<=max; c++, e++)
+ if (tre_toupper(c) != e) break;
+ } else if (tre_isupper(c)) {
+ b = e = tre_tolower(c);
+ for (c++, e++; c<=max; c++, e++)
+ if (tre_tolower(c) != e) break;
+ } else {
+ c++;
+ continue;
+ }
+ lit = tre_new_lit(ls);
+ if (!lit)
+ return -1;
+ lit->code_min = b;
+ lit->code_max = e-1;
+ lit->position = -1;
+ }
+ return 0;
+}
+
+
+/* Maximum number of character classes in a negated bracket expression. */
+#define MAX_NEG_CLASSES 64
+
+struct neg {
+ int negate;
+ int len;
+ tre_ctype_t a[MAX_NEG_CLASSES];
+};
+
+// TODO: parse bracket into a set of non-overlapping [lo,hi] ranges
+
+/*
+bracket grammar:
+Bracket = '[' List ']' | '[^' List ']'
+List = Term | List Term
+Term = Char | Range | Chclass | Eqclass
+Range = Char '-' Char | Char '-' '-'
+Char = Coll | coll_single
+Meta = ']' | '-'
+Coll = '[.' coll_single '.]' | '[.' coll_multi '.]' | '[.' Meta '.]'
+Eqclass = '[=' coll_single '=]' | '[=' coll_multi '=]'
+Chclass = '[:' class ':]'
+
+coll_single is a single char collating element but it can be
+ '-' only at the beginning or end of a List and
+ ']' only at the beginning of a List and
+ '^' anywhere except after the openning '['
+*/
+
+static reg_errcode_t parse_bracket_terms(tre_parse_ctx_t *ctx, const char *s, struct literals *ls, struct neg *neg)
+{
+ const char *start = s;
+ tre_ctype_t class;
+ int min, max;
+ wchar_t wc;
+ int len;
+
+ for (;;) {
+ class = 0;
+ len = mbtowc(&wc, s, -1);
+ if (len <= 0)
+ return *s ? REG_BADPAT : REG_EBRACK;
+ if (*s == ']' && s != start) {
+ ctx->s = s+1;
+ return REG_OK;
+ }
+ if (*s == '-' && s != start && s[1] != ']' &&
+ /* extension: [a-z--@] is accepted as [a-z]|[--@] */
+ (s[1] != '-' || s[2] == ']'))
+ return REG_ERANGE;
+ if (*s == '[' && (s[1] == '.' || s[1] == '='))
+ /* collating symbols and equivalence classes are not supported */
+ return REG_ECOLLATE;
+ if (*s == '[' && s[1] == ':') {
+ char tmp[CHARCLASS_NAME_MAX+1];
+ s += 2;
+ for (len=0; len < CHARCLASS_NAME_MAX && s[len]; len++) {
+ if (s[len] == ':') {
+ memcpy(tmp, s, len);
+ tmp[len] = 0;
+ class = tre_ctype(tmp);
+ break;
+ }
+ }
+ if (!class || s[len+1] != ']')
+ return REG_ECTYPE;
+ min = 0;
+ max = TRE_CHAR_MAX;
+ s += len+2;
+ } else {
+ min = max = wc;
+ s += len;
+ if (*s == '-' && s[1] != ']') {
+ s++;
+ len = mbtowc(&wc, s, -1);
+ max = wc;
+ /* XXX - Should use collation order instead of
+ encoding values in character ranges. */
+ if (len <= 0 || min > max)
+ return REG_ERANGE;
+ s += len;
+ }
+ }
+
+ if (class && neg->negate) {
+ if (neg->len >= MAX_NEG_CLASSES)
+ return REG_ESPACE;
+ neg->a[neg->len++] = class;
+ } else {
+ tre_literal_t *lit = tre_new_lit(ls);
+ if (!lit)
+ return REG_ESPACE;
+ lit->code_min = min;
+ lit->code_max = max;
+ lit->class = class;
+ lit->position = -1;
+
+ /* Add opposite-case codepoints if REG_ICASE is present.
+ It seems that POSIX requires that bracket negation
+ should happen before case-folding, but most practical
+ implementations do it the other way around. Changing
+ the order would need efficient representation of
+ case-fold ranges and bracket range sets even with
+ simple patterns so this is ok for now. */
+ if (ctx->cflags & REG_ICASE && !class)
+ if (add_icase_literals(ls, min, max))
+ return REG_ESPACE;
+ }
+ }
+}
+
+static reg_errcode_t parse_bracket(tre_parse_ctx_t *ctx, const char *s)
+{
+ int i, max, min, negmax, negmin;
+ tre_ast_node_t *node = 0, *n;
+ tre_ctype_t *nc = 0;
+ tre_literal_t *lit;
+ struct literals ls;
+ struct neg neg;
+ reg_errcode_t err;
+
+ ls.mem = ctx->mem;
+ ls.len = 0;
+ ls.cap = 32;
+ ls.a = xmalloc(ls.cap * sizeof *ls.a);
+ if (!ls.a)
+ return REG_ESPACE;
+ neg.len = 0;
+ neg.negate = *s == '^';
+ if (neg.negate)
+ s++;
+
+ err = parse_bracket_terms(ctx, s, &ls, &neg);
+ if (err != REG_OK)
+ goto parse_bracket_done;
+
+ if (neg.negate) {
+ /*
+ * With REG_NEWLINE, POSIX requires that newlines are not matched by
+ * any form of a non-matching list.
+ */
+ if (ctx->cflags & REG_NEWLINE) {
+ lit = tre_new_lit(&ls);
+ if (!lit) {
+ err = REG_ESPACE;
+ goto parse_bracket_done;
+ }
+ lit->code_min = '\n';
+ lit->code_max = '\n';
+ lit->position = -1;
+ }
+ /* Sort the array if we need to negate it. */
+ qsort(ls.a, ls.len, sizeof *ls.a, tre_compare_lit);
+ /* extra lit for the last negated range */
+ lit = tre_new_lit(&ls);
+ if (!lit) {
+ err = REG_ESPACE;
+ goto parse_bracket_done;
+ }
+ lit->code_min = TRE_CHAR_MAX+1;
+ lit->code_max = TRE_CHAR_MAX+1;
+ lit->position = -1;
+ /* negated classes */
+ if (neg.len) {
+ nc = tre_mem_alloc(ctx->mem, (neg.len+1)*sizeof *neg.a);
+ if (!nc) {
+ err = REG_ESPACE;
+ goto parse_bracket_done;
+ }
+ memcpy(nc, neg.a, neg.len*sizeof *neg.a);
+ nc[neg.len] = 0;
+ }
+ }
+
+ /* Build a union of the items in the array, negated if necessary. */
+ negmax = negmin = 0;
+ for (i = 0; i < ls.len; i++) {
+ lit = ls.a[i];
+ min = lit->code_min;
+ max = lit->code_max;
+ if (neg.negate) {
+ if (min <= negmin) {
+ /* Overlap. */
+ negmin = MAX(max + 1, negmin);
+ continue;
+ }
+ negmax = min - 1;
+ lit->code_min = negmin;
+ lit->code_max = negmax;
+ negmin = max + 1;
+ }
+ lit->position = ctx->position;
+ lit->neg_classes = nc;
+ n = tre_ast_new_node(ctx->mem, LITERAL, lit);
+ node = tre_ast_new_union(ctx->mem, node, n);
+ if (!node) {
+ err = REG_ESPACE;
+ break;
+ }
+ }
+
+parse_bracket_done:
+ xfree(ls.a);
+ ctx->position++;
+ ctx->n = node;
+ return err;
+}
+
+static const char *parse_dup_count(const char *s, int *n)
+{
+ *n = -1;
+ if (!isdigit(*s))
+ return s;
+ *n = 0;
+ for (;;) {
+ *n = 10 * *n + (*s - '0');
+ s++;
+ if (!isdigit(*s) || *n > RE_DUP_MAX)
+ break;
+ }
+ return s;
+}
+
+static const char *parse_dup(const char *s, int ere, int *pmin, int *pmax)
+{
+ int min, max;
+
+ s = parse_dup_count(s, &min);
+ if (*s == ',')
+ s = parse_dup_count(s+1, &max);
+ else
+ max = min;
+
+ if (
+ (max < min && max >= 0) ||
+ max > RE_DUP_MAX ||
+ min > RE_DUP_MAX ||
+ min < 0 ||
+ (!ere && *s++ != '\\') ||
+ *s++ != '}'
+ )
+ return 0;
+ *pmin = min;
+ *pmax = max;
+ return s;
+}
+
+static int hexval(unsigned c)
+{
+ if (c-'0'<10) return c-'0';
+ c |= 32;
+ if (c-'a'<6) return c-'a'+10;
+ return -1;
+}
+
+static reg_errcode_t marksub(tre_parse_ctx_t *ctx, tre_ast_node_t *node, int subid)
+{
+ if (node->submatch_id >= 0) {
+ tre_ast_node_t *n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ if (!n)
+ return REG_ESPACE;
+ n = tre_ast_new_catenation(ctx->mem, n, node);
+ if (!n)
+ return REG_ESPACE;
+ n->num_submatches = node->num_submatches;
+ node = n;
+ }
+ node->submatch_id = subid;
+ node->num_submatches++;
+ ctx->n = node;
+ return REG_OK;
+}
+
+/*
+BRE grammar:
+Regex = Branch | '^' | '$' | '^$' | '^' Branch | Branch '$' | '^' Branch '$'
+Branch = Atom | Branch Atom
+Atom = char | quoted_char | '.' | Bracket | Atom Dup | '\(' Branch '\)' | back_ref
+Dup = '*' | '\{' Count '\}' | '\{' Count ',\}' | '\{' Count ',' Count '\}'
+
+(leading ^ and trailing $ in a sub expr may be an anchor or literal as well)
+
+ERE grammar:
+Regex = Branch | Regex '|' Branch
+Branch = Atom | Branch Atom
+Atom = char | quoted_char | '.' | Bracket | Atom Dup | '(' Regex ')' | '^' | '$'
+Dup = '*' | '+' | '?' | '{' Count '}' | '{' Count ',}' | '{' Count ',' Count '}'
+
+(a*+?, ^*, $+, \X, {, (|a) are unspecified)
+*/
+
+static reg_errcode_t parse_atom(tre_parse_ctx_t *ctx, const char *s)
+{
+ int len, ere = ctx->cflags & REG_EXTENDED;
+ const char *p;
+ tre_ast_node_t *node;
+ wchar_t wc;
+ switch (*s) {
+ case '[':
+ return parse_bracket(ctx, s+1);
+ case '\\':
+ p = tre_expand_macro(s+1);
+ if (p) {
+ /* assume \X expansion is a single atom */
+ reg_errcode_t err = parse_atom(ctx, p);
+ ctx->s = s+2;
+ return err;
+ }
+ /* extensions: \b, \B, \<, \>, \xHH \x{HHHH} */
+ switch (*++s) {
+ case 0:
+ return REG_EESCAPE;
+ case 'b':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB, -1);
+ break;
+ case 'B':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB_NEG, -1);
+ break;
+ case '<':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOW, -1);
+ break;
+ case '>':
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOW, -1);
+ break;
+ case 'x':
+ s++;
+ int i, v = 0, c;
+ len = 2;
+ if (*s == '{') {
+ len = 8;
+ s++;
+ }
+ for (i=0; i<len && v<0x110000; i++) {
+ c = hexval(s[i]);
+ if (c < 0) break;
+ v = 16*v + c;
+ }
+ s += i;
+ if (len == 8) {
+ if (*s != '}')
+ return REG_EBRACE;
+ s++;
+ }
+ node = tre_ast_new_literal(ctx->mem, v, v, ctx->position++);
+ s--;
+ break;
+ case '{':
+ case '+':
+ case '?':
+ /* extension: treat \+, \? as repetitions in BRE */
+ /* reject repetitions after empty expression in BRE */
+ if (!ere)
+ return REG_BADRPT;
+ case '|':
+ /* extension: treat \| as alternation in BRE */
+ if (!ere) {
+ node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ s--;
+ goto end;
+ }
+ /* fallthrough */
+ default:
+ if (!ere && (unsigned)*s-'1' < 9) {
+ /* back reference */
+ int val = *s - '0';
+ node = tre_ast_new_literal(ctx->mem, BACKREF, val, ctx->position++);
+ ctx->max_backref = MAX(val, ctx->max_backref);
+ } else {
+ /* extension: accept unknown escaped char
+ as a literal */
+ goto parse_literal;
+ }
+ }
+ s++;
+ break;
+ case '.':
+ if (ctx->cflags & REG_NEWLINE) {
+ tre_ast_node_t *tmp1, *tmp2;
+ tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n'-1, ctx->position++);
+ tmp2 = tre_ast_new_literal(ctx->mem, '\n'+1, TRE_CHAR_MAX, ctx->position++);
+ if (tmp1 && tmp2)
+ node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+ else
+ node = 0;
+ } else {
+ node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position++);
+ }
+ s++;
+ break;
+ case '^':
+ /* '^' has a special meaning everywhere in EREs, and at beginning of BRE. */
+ if (!ere && s != ctx->start)
+ goto parse_literal;
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOL, -1);
+ s++;
+ break;
+ case '$':
+ /* '$' is special everywhere in EREs, and at the end of a BRE subexpression. */
+ if (!ere && s[1] && (s[1]!='\\'|| (s[2]!=')' && s[2]!='|')))
+ goto parse_literal;
+ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOL, -1);
+ s++;
+ break;
+ case '*':
+ case '{':
+ case '+':
+ case '?':
+ /* reject repetitions after empty expression in ERE */
+ if (ere)
+ return REG_BADRPT;
+ case '|':
+ if (!ere)
+ goto parse_literal;
+ case 0:
+ node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ break;
+ default:
+parse_literal:
+ len = mbtowc(&wc, s, -1);
+ if (len < 0)
+ return REG_BADPAT;
+ if (ctx->cflags & REG_ICASE && (tre_isupper(wc) || tre_islower(wc))) {
+ tre_ast_node_t *tmp1, *tmp2;
+ /* multiple opposite case characters are not supported */
+ tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc), tre_toupper(wc), ctx->position);
+ tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc), tre_tolower(wc), ctx->position);
+ if (tmp1 && tmp2)
+ node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+ else
+ node = 0;
+ } else {
+ node = tre_ast_new_literal(ctx->mem, wc, wc, ctx->position);
+ }
+ ctx->position++;
+ s += len;
+ break;
+ }
+end:
+ if (!node)
+ return REG_ESPACE;
+ ctx->n = node;
+ ctx->s = s;
+ return REG_OK;
+}
+
+#define PUSHPTR(err, s, v) do { \
+ if ((err = tre_stack_push_voidptr(s, v)) != REG_OK) \
+ return err; \
+} while(0)
+
+#define PUSHINT(err, s, v) do { \
+ if ((err = tre_stack_push_int(s, v)) != REG_OK) \
+ return err; \
+} while(0)
+
+static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx)
+{
+ tre_ast_node_t *nbranch=0, *nunion=0;
+ int ere = ctx->cflags & REG_EXTENDED;
+ const char *s = ctx->start;
+ int subid = 0;
+ int depth = 0;
+ reg_errcode_t err;
+ tre_stack_t *stack = ctx->stack;
+
+ PUSHINT(err, stack, subid++);
+ for (;;) {
+ if ((!ere && *s == '\\' && s[1] == '(') ||
+ (ere && *s == '(')) {
+ PUSHPTR(err, stack, nunion);
+ PUSHPTR(err, stack, nbranch);
+ PUSHINT(err, stack, subid++);
+ s++;
+ if (!ere)
+ s++;
+ depth++;
+ nbranch = nunion = 0;
+ ctx->start = s;
+ continue;
+ }
+ if ((!ere && *s == '\\' && s[1] == ')') ||
+ (ere && *s == ')' && depth)) {
+ ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ if (!ctx->n)
+ return REG_ESPACE;
+ } else {
+ err = parse_atom(ctx, s);
+ if (err != REG_OK)
+ return err;
+ s = ctx->s;
+ }
+
+ parse_iter:
+ for (;;) {
+ int min, max;
+
+ if (*s!='\\' && *s!='*') {
+ if (!ere)
+ break;
+ if (*s!='+' && *s!='?' && *s!='{')
+ break;
+ }
+ if (*s=='\\' && ere)
+ break;
+ /* extension: treat \+, \? as repetitions in BRE */
+ if (*s=='\\' && s[1]!='+' && s[1]!='?' && s[1]!='{')
+ break;
+ if (*s=='\\')
+ s++;
+
+ /* handle ^* at the start of a BRE. */
+ if (!ere && s==ctx->start+1 && s[-1]=='^')
+ break;
+
+ /* extension: multiple consecutive *+?{,} is unspecified,
+ but (a+)+ has to be supported so accepting a++ makes
+ sense, note however that the RE_DUP_MAX limit can be
+ circumvented: (a{255}){255} uses a lot of memory.. */
+ if (*s=='{') {
+ s = parse_dup(s+1, ere, &min, &max);
+ if (!s)
+ return REG_BADBR;
+ } else {
+ min=0;
+ max=-1;
+ if (*s == '+')
+ min = 1;
+ if (*s == '?')
+ max = 1;
+ s++;
+ }
+ if (max == 0)
+ ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+ else
+ ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0);
+ if (!ctx->n)
+ return REG_ESPACE;
+ }
+
+ nbranch = tre_ast_new_catenation(ctx->mem, nbranch, ctx->n);
+ if ((ere && *s == '|') ||
+ (ere && *s == ')' && depth) ||
+ (!ere && *s == '\\' && s[1] == ')') ||
+ /* extension: treat \| as alternation in BRE */
+ (!ere && *s == '\\' && s[1] == '|') ||
+ !*s) {
+ /* extension: empty branch is unspecified (), (|a), (a|)
+ here they are not rejected but match on empty string */
+ int c = *s;
+ nunion = tre_ast_new_union(ctx->mem, nunion, nbranch);
+ nbranch = 0;
+
+ if (c == '\\' && s[1] == '|') {
+ s+=2;
+ ctx->start = s;
+ } else if (c == '|') {
+ s++;
+ ctx->start = s;
+ } else {
+ if (c == '\\') {
+ if (!depth) return REG_EPAREN;
+ s+=2;
+ } else if (c == ')')
+ s++;
+ depth--;
+ err = marksub(ctx, nunion, tre_stack_pop_int(stack));
+ if (err != REG_OK)
+ return err;
+ if (!c && depth<0) {
+ ctx->submatch_id = subid;
+ return REG_OK;
+ }
+ if (!c || depth<0)
+ return REG_EPAREN;
+ nbranch = tre_stack_pop_voidptr(stack);
+ nunion = tre_stack_pop_voidptr(stack);
+ goto parse_iter;
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+ from tre-compile.c
+***********************************************************************/
+
+
+/*
+ TODO:
+ - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive
+ function calls.
+*/
+
+/*
+ Algorithms to setup tags so that submatch addressing can be done.
+*/
+
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+ As the left child a new tag with number `tag_id' to `node' is added,
+ and the right child is the old root. */
+static reg_errcode_t
+tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+ tre_catenation_t *c;
+
+ c = tre_mem_alloc(mem, sizeof(*c));
+ if (c == NULL)
+ return REG_ESPACE;
+ c->left = tre_ast_new_literal(mem, TAG, tag_id, -1);
+ if (c->left == NULL)
+ return REG_ESPACE;
+ c->right = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+ if (c->right == NULL)
+ return REG_ESPACE;
+
+ c->right->obj = node->obj;
+ c->right->type = node->type;
+ c->right->nullable = -1;
+ c->right->submatch_id = -1;
+ c->right->firstpos = NULL;
+ c->right->lastpos = NULL;
+ c->right->num_tags = 0;
+ c->right->num_submatches = 0;
+ node->obj = c;
+ node->type = CATENATION;
+ return REG_OK;
+}
+
+/* Inserts a catenation node to the root of the tree given in `node'.
+ As the right child a new tag with number `tag_id' to `node' is added,
+ and the left child is the old root. */
+static reg_errcode_t
+tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id)
+{
+ tre_catenation_t *c;
+
+ c = tre_mem_alloc(mem, sizeof(*c));
+ if (c == NULL)
+ return REG_ESPACE;
+ c->right = tre_ast_new_literal(mem, TAG, tag_id, -1);
+ if (c->right == NULL)
+ return REG_ESPACE;
+ c->left = tre_mem_alloc(mem, sizeof(tre_ast_node_t));
+ if (c->left == NULL)
+ return REG_ESPACE;
+
+ c->left->obj = node->obj;
+ c->left->type = node->type;
+ c->left->nullable = -1;
+ c->left->submatch_id = -1;
+ c->left->firstpos = NULL;
+ c->left->lastpos = NULL;
+ c->left->num_tags = 0;
+ c->left->num_submatches = 0;
+ node->obj = c;
+ node->type = CATENATION;
+ return REG_OK;
+}
+
+typedef enum {
+ ADDTAGS_RECURSE,
+ ADDTAGS_AFTER_ITERATION,
+ ADDTAGS_AFTER_UNION_LEFT,
+ ADDTAGS_AFTER_UNION_RIGHT,
+ ADDTAGS_AFTER_CAT_LEFT,
+ ADDTAGS_AFTER_CAT_RIGHT,
+ ADDTAGS_SET_SUBMATCH_END
+} tre_addtags_symbol_t;
+
+
+typedef struct {
+ int tag;
+ int next_tag;
+} tre_tag_states_t;
+
+
+/* Go through `regset' and set submatch data for submatches that are
+ using this tag. */
+static void
+tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag)
+{
+ int i;
+
+ for (i = 0; regset[i] >= 0; i++)
+ {
+ int id = regset[i] / 2;
+ int start = !(regset[i] % 2);
+ if (start)
+ tnfa->submatch_data[id].so_tag = tag;
+ else
+ tnfa->submatch_data[id].eo_tag = tag;
+ }
+ regset[0] = -1;
+}
+
+
+/* Adds tags to appropriate locations in the parse tree in `tree', so that
+ subexpressions marked for submatch addressing can be traced. */
+static reg_errcode_t
+tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree,
+ tre_tnfa_t *tnfa)
+{
+ reg_errcode_t status = REG_OK;
+ tre_addtags_symbol_t symbol;
+ tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */
+ int bottom = tre_stack_num_objects(stack);
+ /* True for first pass (counting number of needed tags) */
+ int first_pass = (mem == NULL || tnfa == NULL);
+ int *regset, *orig_regset;
+ int num_tags = 0; /* Total number of tags. */
+ int num_minimals = 0; /* Number of special minimal tags. */
+ int tag = 0; /* The tag that is to be added next. */
+ int next_tag = 1; /* Next tag to use after this one. */
+ int *parents; /* Stack of submatches the current submatch is
+ contained in. */
+ int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */
+ tre_tag_states_t *saved_states;
+
+ tre_tag_direction_t direction = TRE_TAG_MINIMIZE;
+ if (!first_pass)
+ {
+ tnfa->end_tag = 0;
+ tnfa->minimal_tags[0] = -1;
+ }
+
+ regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2));
+ if (regset == NULL)
+ return REG_ESPACE;
+ regset[0] = -1;
+ orig_regset = regset;
+
+ parents = xmalloc(sizeof(*parents) * (tnfa->num_submatches + 1));
+ if (parents == NULL)
+ {
+ xfree(regset);
+ return REG_ESPACE;
+ }
+ parents[0] = -1;
+
+ saved_states = xmalloc(sizeof(*saved_states) * (tnfa->num_submatches + 1));
+ if (saved_states == NULL)
+ {
+ xfree(regset);
+ xfree(parents);
+ return REG_ESPACE;
+ }
+ else
+ {
+ unsigned int i;
+ for (i = 0; i <= tnfa->num_submatches; i++)
+ saved_states[i].tag = -1;
+ }
+
+ STACK_PUSH(stack, voidptr, node);
+ STACK_PUSH(stack, int, ADDTAGS_RECURSE);
+
+ while (tre_stack_num_objects(stack) > bottom)
+ {
+ if (status != REG_OK)
+ break;
+
+ symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack);
+ switch (symbol)
+ {
+
+ case ADDTAGS_SET_SUBMATCH_END:
+ {
+ int id = tre_stack_pop_int(stack);
+ int i;
+
+ /* Add end of this submatch to regset. */
+ for (i = 0; regset[i] >= 0; i++);
+ regset[i] = id * 2 + 1;
+ regset[i + 1] = -1;
+
+ /* Pop this submatch from the parents stack. */
+ for (i = 0; parents[i] >= 0; i++);
+ parents[i - 1] = -1;
+ break;
+ }
+
+ case ADDTAGS_RECURSE:
+ node = tre_stack_pop_voidptr(stack);
+
+ if (node->submatch_id >= 0)
+ {
+ int id = node->submatch_id;
+ int i;
+
+
+ /* Add start of this submatch to regset. */
+ for (i = 0; regset[i] >= 0; i++);
+ regset[i] = id * 2;
+ regset[i + 1] = -1;
+
+ if (!first_pass)
+ {
+ for (i = 0; parents[i] >= 0; i++);
+ tnfa->submatch_data[id].parents = NULL;
+ if (i > 0)
+ {
+ int *p = xmalloc(sizeof(*p) * (i + 1));
+ if (p == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ assert(tnfa->submatch_data[id].parents == NULL);
+ tnfa->submatch_data[id].parents = p;
+ for (i = 0; parents[i] >= 0; i++)
+ p[i] = parents[i];
+ p[i] = -1;
+ }
+ }
+
+ /* Add end of this submatch to regset after processing this
+ node. */
+ STACK_PUSHX(stack, int, node->submatch_id);
+ STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END);
+ }
+
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit = node->obj;
+
+ if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+ {
+ int i;
+ if (regset[0] >= 0)
+ {
+ /* Regset is not empty, so add a tag before the
+ literal or backref. */
+ if (!first_pass)
+ {
+ status = tre_add_tag_left(mem, node, tag);
+ tnfa->tag_directions[tag] = direction;
+ if (minimal_tag >= 0)
+ {
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+ tre_purge_regset(regset, tnfa, tag);
+ }
+ else
+ {
+ node->num_tags = 1;
+ }
+
+ regset[0] = -1;
+ tag = next_tag;
+ num_tags++;
+ next_tag++;
+ }
+ }
+ else
+ {
+ assert(!IS_TAG(lit));
+ }
+ break;
+ }
+ case CATENATION:
+ {
+ tre_catenation_t *cat = node->obj;
+ tre_ast_node_t *left = cat->left;
+ tre_ast_node_t *right = cat->right;
+ int reserved_tag = -1;
+
+
+ /* After processing right child. */
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT);
+
+ /* Process right child. */
+ STACK_PUSHX(stack, voidptr, right);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* After processing left child. */
+ STACK_PUSHX(stack, int, next_tag + left->num_tags);
+ if (left->num_tags > 0 && right->num_tags > 0)
+ {
+ /* Reserve the next tag to the right child. */
+ reserved_tag = next_tag;
+ next_tag++;
+ }
+ STACK_PUSHX(stack, int, reserved_tag);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT);
+
+ /* Process left child. */
+ STACK_PUSHX(stack, voidptr, left);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ }
+ break;
+ case ITERATION:
+ {
+ tre_iteration_t *iter = node->obj;
+
+ if (first_pass)
+ {
+ STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal);
+ }
+ else
+ {
+ STACK_PUSHX(stack, int, tag);
+ STACK_PUSHX(stack, int, iter->minimal);
+ }
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION);
+
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* Regset is not empty, so add a tag here. */
+ if (regset[0] >= 0 || iter->minimal)
+ {
+ if (!first_pass)
+ {
+ int i;
+ status = tre_add_tag_left(mem, node, tag);
+ if (iter->minimal)
+ tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE;
+ else
+ tnfa->tag_directions[tag] = direction;
+ if (minimal_tag >= 0)
+ {
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+ tre_purge_regset(regset, tnfa, tag);
+ }
+
+ regset[0] = -1;
+ tag = next_tag;
+ num_tags++;
+ next_tag++;
+ }
+ direction = TRE_TAG_MINIMIZE;
+ }
+ break;
+ case UNION:
+ {
+ tre_union_t *uni = node->obj;
+ tre_ast_node_t *left = uni->left;
+ tre_ast_node_t *right = uni->right;
+ int left_tag;
+ int right_tag;
+
+ if (regset[0] >= 0)
+ {
+ left_tag = next_tag;
+ right_tag = next_tag + 1;
+ }
+ else
+ {
+ left_tag = tag;
+ right_tag = next_tag;
+ }
+
+ /* After processing right child. */
+ STACK_PUSHX(stack, int, right_tag);
+ STACK_PUSHX(stack, int, left_tag);
+ STACK_PUSHX(stack, voidptr, regset);
+ STACK_PUSHX(stack, int, regset[0] >= 0);
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, voidptr, right);
+ STACK_PUSHX(stack, voidptr, left);
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT);
+
+ /* Process right child. */
+ STACK_PUSHX(stack, voidptr, right);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* After processing left child. */
+ STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT);
+
+ /* Process left child. */
+ STACK_PUSHX(stack, voidptr, left);
+ STACK_PUSHX(stack, int, ADDTAGS_RECURSE);
+
+ /* Regset is not empty, so add a tag here. */
+ if (regset[0] >= 0)
+ {
+ if (!first_pass)
+ {
+ int i;
+ status = tre_add_tag_left(mem, node, tag);
+ tnfa->tag_directions[tag] = direction;
+ if (minimal_tag >= 0)
+ {
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+ tre_purge_regset(regset, tnfa, tag);
+ }
+
+ regset[0] = -1;
+ tag = next_tag;
+ num_tags++;
+ next_tag++;
+ }
+
+ if (node->num_submatches > 0)
+ {
+ /* The next two tags are reserved for markers. */
+ next_tag++;
+ tag = next_tag;
+ next_tag++;
+ }
+
+ break;
+ }
+ }
+
+ if (node->submatch_id >= 0)
+ {
+ int i;
+ /* Push this submatch on the parents stack. */
+ for (i = 0; parents[i] >= 0; i++);
+ parents[i] = node->submatch_id;
+ parents[i + 1] = -1;
+ }
+
+ break; /* end case: ADDTAGS_RECURSE */
+
+ case ADDTAGS_AFTER_ITERATION:
+ {
+ int minimal = 0;
+ int enter_tag;
+ node = tre_stack_pop_voidptr(stack);
+ if (first_pass)
+ {
+ node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags
+ + tre_stack_pop_int(stack);
+ minimal_tag = -1;
+ }
+ else
+ {
+ minimal = tre_stack_pop_int(stack);
+ enter_tag = tre_stack_pop_int(stack);
+ if (minimal)
+ minimal_tag = enter_tag;
+ }
+
+ if (!first_pass)
+ {
+ if (minimal)
+ direction = TRE_TAG_MINIMIZE;
+ else
+ direction = TRE_TAG_MAXIMIZE;
+ }
+ break;
+ }
+
+ case ADDTAGS_AFTER_CAT_LEFT:
+ {
+ int new_tag = tre_stack_pop_int(stack);
+ next_tag = tre_stack_pop_int(stack);
+ if (new_tag >= 0)
+ {
+ tag = new_tag;
+ }
+ break;
+ }
+
+ case ADDTAGS_AFTER_CAT_RIGHT:
+ node = tre_stack_pop_voidptr(stack);
+ if (first_pass)
+ node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags
+ + ((tre_catenation_t *)node->obj)->right->num_tags;
+ break;
+
+ case ADDTAGS_AFTER_UNION_LEFT:
+ /* Lift the bottom of the `regset' array so that when processing
+ the right operand the items currently in the array are
+ invisible. The original bottom was saved at ADDTAGS_UNION and
+ will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */
+ while (*regset >= 0)
+ regset++;
+ break;
+
+ case ADDTAGS_AFTER_UNION_RIGHT:
+ {
+ int added_tags, tag_left, tag_right;
+ tre_ast_node_t *left = tre_stack_pop_voidptr(stack);
+ tre_ast_node_t *right = tre_stack_pop_voidptr(stack);
+ node = tre_stack_pop_voidptr(stack);
+ added_tags = tre_stack_pop_int(stack);
+ if (first_pass)
+ {
+ node->num_tags = ((tre_union_t *)node->obj)->left->num_tags
+ + ((tre_union_t *)node->obj)->right->num_tags + added_tags
+ + ((node->num_submatches > 0) ? 2 : 0);
+ }
+ regset = tre_stack_pop_voidptr(stack);
+ tag_left = tre_stack_pop_int(stack);
+ tag_right = tre_stack_pop_int(stack);
+
+ /* Add tags after both children, the left child gets a smaller
+ tag than the right child. This guarantees that we prefer
+ the left child over the right child. */
+ /* XXX - This is not always necessary (if the children have
+ tags which must be seen for every match of that child). */
+ /* XXX - Check if this is the only place where tre_add_tag_right
+ is used. If so, use tre_add_tag_left (putting the tag before
+ the child as opposed after the child) and throw away
+ tre_add_tag_right. */
+ if (node->num_submatches > 0)
+ {
+ if (!first_pass)
+ {
+ status = tre_add_tag_right(mem, left, tag_left);
+ tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE;
+ if (status == REG_OK)
+ status = tre_add_tag_right(mem, right, tag_right);
+ tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE;
+ }
+ num_tags += 2;
+ }
+ direction = TRE_TAG_MAXIMIZE;
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+
+ } /* end switch(symbol) */
+ } /* end while(tre_stack_num_objects(stack) > bottom) */
+
+ if (!first_pass)
+ tre_purge_regset(regset, tnfa, tag);
+
+ if (!first_pass && minimal_tag >= 0)
+ {
+ int i;
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i++);
+ tnfa->minimal_tags[i] = tag;
+ tnfa->minimal_tags[i + 1] = minimal_tag;
+ tnfa->minimal_tags[i + 2] = -1;
+ minimal_tag = -1;
+ num_minimals++;
+ }
+
+ assert(tree->num_tags == num_tags);
+ tnfa->end_tag = num_tags;
+ tnfa->num_tags = num_tags;
+ tnfa->num_minimals = num_minimals;
+ xfree(orig_regset);
+ xfree(parents);
+ xfree(saved_states);
+ return status;
+}
+
+
+
+/*
+ AST to TNFA compilation routines.
+*/
+
+typedef enum {
+ COPY_RECURSE,
+ COPY_SET_RESULT_PTR
+} tre_copyast_symbol_t;
+
+/* Flags for tre_copy_ast(). */
+#define COPY_REMOVE_TAGS 1
+#define COPY_MAXIMIZE_FIRST_TAG 2
+
+static reg_errcode_t
+tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+ int flags, int *pos_add, tre_tag_direction_t *tag_directions,
+ tre_ast_node_t **copy, int *max_pos)
+{
+ reg_errcode_t status = REG_OK;
+ int bottom = tre_stack_num_objects(stack);
+ int num_copied = 0;
+ int first_tag = 1;
+ tre_ast_node_t **result = copy;
+ tre_copyast_symbol_t symbol;
+
+ STACK_PUSH(stack, voidptr, ast);
+ STACK_PUSH(stack, int, COPY_RECURSE);
+
+ while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+ {
+ tre_ast_node_t *node;
+ if (status != REG_OK)
+ break;
+
+ symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack);
+ switch (symbol)
+ {
+ case COPY_SET_RESULT_PTR:
+ result = tre_stack_pop_voidptr(stack);
+ break;
+ case COPY_RECURSE:
+ node = tre_stack_pop_voidptr(stack);
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit = node->obj;
+ int pos = lit->position;
+ int min = lit->code_min;
+ int max = lit->code_max;
+ if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+ {
+ /* XXX - e.g. [ab] has only one position but two
+ nodes, so we are creating holes in the state space
+ here. Not fatal, just wastes memory. */
+ pos += *pos_add;
+ num_copied++;
+ }
+ else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS))
+ {
+ /* Change this tag to empty. */
+ min = EMPTY;
+ max = pos = -1;
+ }
+ else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG)
+ && first_tag)
+ {
+ /* Maximize the first tag. */
+ tag_directions[max] = TRE_TAG_MAXIMIZE;
+ first_tag = 0;
+ }
+ *result = tre_ast_new_literal(mem, min, max, pos);
+ if (*result == NULL)
+ status = REG_ESPACE;
+ else {
+ tre_literal_t *p = (*result)->obj;
+ p->class = lit->class;
+ p->neg_classes = lit->neg_classes;
+ }
+
+ if (pos > *max_pos)
+ *max_pos = pos;
+ break;
+ }
+ case UNION:
+ {
+ tre_union_t *uni = node->obj;
+ tre_union_t *tmp;
+ *result = tre_ast_new_union(mem, uni->left, uni->right);
+ if (*result == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ tmp = (*result)->obj;
+ result = &tmp->left;
+ STACK_PUSHX(stack, voidptr, uni->right);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ STACK_PUSHX(stack, voidptr, &tmp->right);
+ STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+ STACK_PUSHX(stack, voidptr, uni->left);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ break;
+ }
+ case CATENATION:
+ {
+ tre_catenation_t *cat = node->obj;
+ tre_catenation_t *tmp;
+ *result = tre_ast_new_catenation(mem, cat->left, cat->right);
+ if (*result == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ tmp = (*result)->obj;
+ tmp->left = NULL;
+ tmp->right = NULL;
+ result = &tmp->left;
+
+ STACK_PUSHX(stack, voidptr, cat->right);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ STACK_PUSHX(stack, voidptr, &tmp->right);
+ STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR);
+ STACK_PUSHX(stack, voidptr, cat->left);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ break;
+ }
+ case ITERATION:
+ {
+ tre_iteration_t *iter = node->obj;
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ STACK_PUSHX(stack, int, COPY_RECURSE);
+ *result = tre_ast_new_iter(mem, iter->arg, iter->min,
+ iter->max, iter->minimal);
+ if (*result == NULL)
+ {
+ status = REG_ESPACE;
+ break;
+ }
+ iter = (*result)->obj;
+ result = &iter->arg;
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ break;
+ }
+ }
+ *pos_add += num_copied;
+ return status;
+}
+
+typedef enum {
+ EXPAND_RECURSE,
+ EXPAND_AFTER_ITER
+} tre_expand_ast_symbol_t;
+
+/* Expands each iteration node that has a finite nonzero minimum or maximum
+ iteration count to a catenated sequence of copies of the node. */
+static reg_errcode_t
+tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast,
+ int *position, tre_tag_direction_t *tag_directions)
+{
+ reg_errcode_t status = REG_OK;
+ int bottom = tre_stack_num_objects(stack);
+ int pos_add = 0;
+ int pos_add_total = 0;
+ int max_pos = 0;
+ int iter_depth = 0;
+
+ STACK_PUSHR(stack, voidptr, ast);
+ STACK_PUSHR(stack, int, EXPAND_RECURSE);
+ while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+ {
+ tre_ast_node_t *node;
+ tre_expand_ast_symbol_t symbol;
+
+ if (status != REG_OK)
+ break;
+
+ symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack);
+ node = tre_stack_pop_voidptr(stack);
+ switch (symbol)
+ {
+ case EXPAND_RECURSE:
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit= node->obj;
+ if (!IS_SPECIAL(lit) || IS_BACKREF(lit))
+ {
+ lit->position += pos_add;
+ if (lit->position > max_pos)
+ max_pos = lit->position;
+ }
+ break;
+ }
+ case UNION:
+ {
+ tre_union_t *uni = node->obj;
+ STACK_PUSHX(stack, voidptr, uni->right);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ STACK_PUSHX(stack, voidptr, uni->left);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ break;
+ }
+ case CATENATION:
+ {
+ tre_catenation_t *cat = node->obj;
+ STACK_PUSHX(stack, voidptr, cat->right);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ STACK_PUSHX(stack, voidptr, cat->left);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ break;
+ }
+ case ITERATION:
+ {
+ tre_iteration_t *iter = node->obj;
+ STACK_PUSHX(stack, int, pos_add);
+ STACK_PUSHX(stack, voidptr, node);
+ STACK_PUSHX(stack, int, EXPAND_AFTER_ITER);
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ STACK_PUSHX(stack, int, EXPAND_RECURSE);
+ /* If we are going to expand this node at EXPAND_AFTER_ITER
+ then don't increase the `pos' fields of the nodes now, it
+ will get done when expanding. */
+ if (iter->min > 1 || iter->max > 1)
+ pos_add = 0;
+ iter_depth++;
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ break;
+ case EXPAND_AFTER_ITER:
+ {
+ tre_iteration_t *iter = node->obj;
+ int pos_add_last;
+ pos_add = tre_stack_pop_int(stack);
+ pos_add_last = pos_add;
+ if (iter->min > 1 || iter->max > 1)
+ {
+ tre_ast_node_t *seq1 = NULL, *seq2 = NULL;
+ int j;
+ int pos_add_save = pos_add;
+
+ /* Create a catenated sequence of copies of the node. */
+ for (j = 0; j < iter->min; j++)
+ {
+ tre_ast_node_t *copy;
+ /* Remove tags from all but the last copy. */
+ int flags = ((j + 1 < iter->min)
+ ? COPY_REMOVE_TAGS
+ : COPY_MAXIMIZE_FIRST_TAG);
+ pos_add_save = pos_add;
+ status = tre_copy_ast(mem, stack, iter->arg, flags,
+ &pos_add, tag_directions, &copy,
+ &max_pos);
+ if (status != REG_OK)
+ return status;
+ if (seq1 != NULL)
+ seq1 = tre_ast_new_catenation(mem, seq1, copy);
+ else
+ seq1 = copy;
+ if (seq1 == NULL)
+ return REG_ESPACE;
+ }
+
+ if (iter->max == -1)
+ {
+ /* No upper limit. */
+ pos_add_save = pos_add;
+ status = tre_copy_ast(mem, stack, iter->arg, 0,
+ &pos_add, NULL, &seq2, &max_pos);
+ if (status != REG_OK)
+ return status;
+ seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0);
+ if (seq2 == NULL)
+ return REG_ESPACE;
+ }
+ else
+ {
+ for (j = iter->min; j < iter->max; j++)
+ {
+ tre_ast_node_t *tmp, *copy;
+ pos_add_save = pos_add;
+ status = tre_copy_ast(mem, stack, iter->arg, 0,
+ &pos_add, NULL, &copy, &max_pos);
+ if (status != REG_OK)
+ return status;
+ if (seq2 != NULL)
+ seq2 = tre_ast_new_catenation(mem, copy, seq2);
+ else
+ seq2 = copy;
+ if (seq2 == NULL)
+ return REG_ESPACE;
+ tmp = tre_ast_new_literal(mem, EMPTY, -1, -1);
+ if (tmp == NULL)
+ return REG_ESPACE;
+ seq2 = tre_ast_new_union(mem, tmp, seq2);
+ if (seq2 == NULL)
+ return REG_ESPACE;
+ }
+ }
+
+ pos_add = pos_add_save;
+ if (seq1 == NULL)
+ seq1 = seq2;
+ else if (seq2 != NULL)
+ seq1 = tre_ast_new_catenation(mem, seq1, seq2);
+ if (seq1 == NULL)
+ return REG_ESPACE;
+ node->obj = seq1->obj;
+ node->type = seq1->type;
+ }
+
+ iter_depth--;
+ pos_add_total += pos_add - pos_add_last;
+ if (iter_depth == 0)
+ pos_add = pos_add_total;
+
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ *position += pos_add_total;
+
+ /* `max_pos' should never be larger than `*position' if the above
+ code works, but just an extra safeguard let's make sure
+ `*position' is set large enough so enough memory will be
+ allocated for the transition table. */
+ if (max_pos > *position)
+ *position = max_pos;
+
+ return status;
+}
+
+static tre_pos_and_tags_t *
+tre_set_empty(tre_mem_t mem)
+{
+ tre_pos_and_tags_t *new_set;
+
+ new_set = tre_mem_calloc(mem, sizeof(*new_set));
+ if (new_set == NULL)
+ return NULL;
+
+ new_set[0].position = -1;
+ new_set[0].code_min = -1;
+ new_set[0].code_max = -1;
+
+ return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_one(tre_mem_t mem, int position, int code_min, int code_max,
+ tre_ctype_t class, tre_ctype_t *neg_classes, int backref)
+{
+ tre_pos_and_tags_t *new_set;
+
+ new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2);
+ if (new_set == NULL)
+ return NULL;
+
+ new_set[0].position = position;
+ new_set[0].code_min = code_min;
+ new_set[0].code_max = code_max;
+ new_set[0].class = class;
+ new_set[0].neg_classes = neg_classes;
+ new_set[0].backref = backref;
+ new_set[1].position = -1;
+ new_set[1].code_min = -1;
+ new_set[1].code_max = -1;
+
+ return new_set;
+}
+
+static tre_pos_and_tags_t *
+tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2,
+ int *tags, int assertions)
+{
+ int s1, s2, i, j;
+ tre_pos_and_tags_t *new_set;
+ int *new_tags;
+ int num_tags;
+
+ for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++);
+ for (s1 = 0; set1[s1].position >= 0; s1++);
+ for (s2 = 0; set2[s2].position >= 0; s2++);
+ new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1));
+ if (!new_set )
+ return NULL;
+
+ for (s1 = 0; set1[s1].position >= 0; s1++)
+ {
+ new_set[s1].position = set1[s1].position;
+ new_set[s1].code_min = set1[s1].code_min;
+ new_set[s1].code_max = set1[s1].code_max;
+ new_set[s1].assertions = set1[s1].assertions | assertions;
+ new_set[s1].class = set1[s1].class;
+ new_set[s1].neg_classes = set1[s1].neg_classes;
+ new_set[s1].backref = set1[s1].backref;
+ if (set1[s1].tags == NULL && tags == NULL)
+ new_set[s1].tags = NULL;
+ else
+ {
+ for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++);
+ new_tags = tre_mem_alloc(mem, (sizeof(*new_tags)
+ * (i + num_tags + 1)));
+ if (new_tags == NULL)
+ return NULL;
+ for (j = 0; j < i; j++)
+ new_tags[j] = set1[s1].tags[j];
+ for (i = 0; i < num_tags; i++)
+ new_tags[j + i] = tags[i];
+ new_tags[j + i] = -1;
+ new_set[s1].tags = new_tags;
+ }
+ }
+
+ for (s2 = 0; set2[s2].position >= 0; s2++)
+ {
+ new_set[s1 + s2].position = set2[s2].position;
+ new_set[s1 + s2].code_min = set2[s2].code_min;
+ new_set[s1 + s2].code_max = set2[s2].code_max;
+ /* XXX - why not | assertions here as well? */
+ new_set[s1 + s2].assertions = set2[s2].assertions;
+ new_set[s1 + s2].class = set2[s2].class;
+ new_set[s1 + s2].neg_classes = set2[s2].neg_classes;
+ new_set[s1 + s2].backref = set2[s2].backref;
+ if (set2[s2].tags == NULL)
+ new_set[s1 + s2].tags = NULL;
+ else
+ {
+ for (i = 0; set2[s2].tags[i] >= 0; i++);
+ new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1));
+ if (new_tags == NULL)
+ return NULL;
+ for (j = 0; j < i; j++)
+ new_tags[j] = set2[s2].tags[j];
+ new_tags[j] = -1;
+ new_set[s1 + s2].tags = new_tags;
+ }
+ }
+ new_set[s1 + s2].position = -1;
+ return new_set;
+}
+
+/* Finds the empty path through `node' which is the one that should be
+ taken according to POSIX.2 rules, and adds the tags on that path to
+ `tags'. `tags' may be NULL. If `num_tags_seen' is not NULL, it is
+ set to the number of tags seen on the path. */
+static reg_errcode_t
+tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags,
+ int *assertions, int *num_tags_seen)
+{
+ tre_literal_t *lit;
+ tre_union_t *uni;
+ tre_catenation_t *cat;
+ tre_iteration_t *iter;
+ int i;
+ int bottom = tre_stack_num_objects(stack);
+ reg_errcode_t status = REG_OK;
+ if (num_tags_seen)
+ *num_tags_seen = 0;
+
+ status = tre_stack_push_voidptr(stack, node);
+
+ /* Walk through the tree recursively. */
+ while (status == REG_OK && tre_stack_num_objects(stack) > bottom)
+ {
+ node = tre_stack_pop_voidptr(stack);
+
+ switch (node->type)
+ {
+ case LITERAL:
+ lit = (tre_literal_t *)node->obj;
+ switch (lit->code_min)
+ {
+ case TAG:
+ if (lit->code_max >= 0)
+ {
+ if (tags != NULL)
+ {
+ /* Add the tag to `tags'. */
+ for (i = 0; tags[i] >= 0; i++)
+ if (tags[i] == lit->code_max)
+ break;
+ if (tags[i] < 0)
+ {
+ tags[i] = lit->code_max;
+ tags[i + 1] = -1;
+ }
+ }
+ if (num_tags_seen)
+ (*num_tags_seen)++;
+ }
+ break;
+ case ASSERTION:
+ assert(lit->code_max >= 1
+ || lit->code_max <= ASSERT_LAST);
+ if (assertions != NULL)
+ *assertions |= lit->code_max;
+ break;
+ case EMPTY:
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ break;
+
+ case UNION:
+ /* Subexpressions starting earlier take priority over ones
+ starting later, so we prefer the left subexpression over the
+ right subexpression. */
+ uni = (tre_union_t *)node->obj;
+ if (uni->left->nullable)
+ STACK_PUSHX(stack, voidptr, uni->left)
+ else if (uni->right->nullable)
+ STACK_PUSHX(stack, voidptr, uni->right)
+ else
+ assert(0);
+ break;
+
+ case CATENATION:
+ /* The path must go through both children. */
+ cat = (tre_catenation_t *)node->obj;
+ assert(cat->left->nullable);
+ assert(cat->right->nullable);
+ STACK_PUSHX(stack, voidptr, cat->left);
+ STACK_PUSHX(stack, voidptr, cat->right);
+ break;
+
+ case ITERATION:
+ /* A match with an empty string is preferred over no match at
+ all, so we go through the argument if possible. */
+ iter = (tre_iteration_t *)node->obj;
+ if (iter->arg->nullable)
+ STACK_PUSHX(stack, voidptr, iter->arg);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+typedef enum {
+ NFL_RECURSE,
+ NFL_POST_UNION,
+ NFL_POST_CATENATION,
+ NFL_POST_ITERATION
+} tre_nfl_stack_symbol_t;
+
+
+/* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for
+ the nodes of the AST `tree'. */
+static reg_errcode_t
+tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree)
+{
+ int bottom = tre_stack_num_objects(stack);
+
+ STACK_PUSHR(stack, voidptr, tree);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+
+ while (tre_stack_num_objects(stack) > bottom)
+ {
+ tre_nfl_stack_symbol_t symbol;
+ tre_ast_node_t *node;
+
+ symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack);
+ node = tre_stack_pop_voidptr(stack);
+ switch (symbol)
+ {
+ case NFL_RECURSE:
+ switch (node->type)
+ {
+ case LITERAL:
+ {
+ tre_literal_t *lit = (tre_literal_t *)node->obj;
+ if (IS_BACKREF(lit))
+ {
+ /* Back references: nullable = false, firstpos = {i},
+ lastpos = {i}. */
+ node->nullable = 0;
+ node->firstpos = tre_set_one(mem, lit->position, 0,
+ TRE_CHAR_MAX, 0, NULL, -1);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_one(mem, lit->position, 0,
+ TRE_CHAR_MAX, 0, NULL,
+ (int)lit->code_max);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ else if (lit->code_min < 0)
+ {
+ /* Tags, empty strings, params, and zero width assertions:
+ nullable = true, firstpos = {}, and lastpos = {}. */
+ node->nullable = 1;
+ node->firstpos = tre_set_empty(mem);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_empty(mem);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ else
+ {
+ /* Literal at position i: nullable = false, firstpos = {i},
+ lastpos = {i}. */
+ node->nullable = 0;
+ node->firstpos =
+ tre_set_one(mem, lit->position, (int)lit->code_min,
+ (int)lit->code_max, 0, NULL, -1);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_one(mem, lit->position,
+ (int)lit->code_min,
+ (int)lit->code_max,
+ lit->class, lit->neg_classes,
+ -1);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ break;
+ }
+
+ case UNION:
+ /* Compute the attributes for the two subtrees, and after that
+ for this node. */
+ STACK_PUSHR(stack, voidptr, node);
+ STACK_PUSHR(stack, int, NFL_POST_UNION);
+ STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ break;
+
+ case CATENATION:
+ /* Compute the attributes for the two subtrees, and after that
+ for this node. */
+ STACK_PUSHR(stack, voidptr, node);
+ STACK_PUSHR(stack, int, NFL_POST_CATENATION);
+ STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ break;
+
+ case ITERATION:
+ /* Compute the attributes for the subtree, and after that for
+ this node. */
+ STACK_PUSHR(stack, voidptr, node);
+ STACK_PUSHR(stack, int, NFL_POST_ITERATION);
+ STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg);
+ STACK_PUSHR(stack, int, NFL_RECURSE);
+ break;
+ }
+ break; /* end case: NFL_RECURSE */
+
+ case NFL_POST_UNION:
+ {
+ tre_union_t *uni = (tre_union_t *)node->obj;
+ node->nullable = uni->left->nullable || uni->right->nullable;
+ node->firstpos = tre_set_union(mem, uni->left->firstpos,
+ uni->right->firstpos, NULL, 0);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ node->lastpos = tre_set_union(mem, uni->left->lastpos,
+ uni->right->lastpos, NULL, 0);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ break;
+ }
+
+ case NFL_POST_ITERATION:
+ {
+ tre_iteration_t *iter = (tre_iteration_t *)node->obj;
+
+ if (iter->min == 0 || iter->arg->nullable)
+ node->nullable = 1;
+ else
+ node->nullable = 0;
+ node->firstpos = iter->arg->firstpos;
+ node->lastpos = iter->arg->lastpos;
+ break;
+ }
+
+ case NFL_POST_CATENATION:
+ {
+ int num_tags, *tags, assertions;
+ reg_errcode_t status;
+ tre_catenation_t *cat = node->obj;
+ node->nullable = cat->left->nullable && cat->right->nullable;
+
+ /* Compute firstpos. */
+ if (cat->left->nullable)
+ {
+ /* The left side matches the empty string. Make a first pass
+ with tre_match_empty() to get the number of tags and
+ parameters. */
+ status = tre_match_empty(stack, cat->left,
+ NULL, NULL, &num_tags);
+ if (status != REG_OK)
+ return status;
+ /* Allocate arrays for the tags and parameters. */
+ tags = xmalloc(sizeof(*tags) * (num_tags + 1));
+ if (!tags)
+ return REG_ESPACE;
+ tags[0] = -1;
+ assertions = 0;
+ /* Second pass with tre_mach_empty() to get the list of
+ tags and parameters. */
+ status = tre_match_empty(stack, cat->left, tags,
+ &assertions, NULL);
+ if (status != REG_OK)
+ {
+ xfree(tags);
+ return status;
+ }
+ node->firstpos =
+ tre_set_union(mem, cat->right->firstpos, cat->left->firstpos,
+ tags, assertions);
+ xfree(tags);
+ if (!node->firstpos)
+ return REG_ESPACE;
+ }
+ else
+ {
+ node->firstpos = cat->left->firstpos;
+ }
+
+ /* Compute lastpos. */
+ if (cat->right->nullable)
+ {
+ /* The right side matches the empty string. Make a first pass
+ with tre_match_empty() to get the number of tags and
+ parameters. */
+ status = tre_match_empty(stack, cat->right,
+ NULL, NULL, &num_tags);
+ if (status != REG_OK)
+ return status;
+ /* Allocate arrays for the tags and parameters. */
+ tags = xmalloc(sizeof(int) * (num_tags + 1));
+ if (!tags)
+ return REG_ESPACE;
+ tags[0] = -1;
+ assertions = 0;
+ /* Second pass with tre_mach_empty() to get the list of
+ tags and parameters. */
+ status = tre_match_empty(stack, cat->right, tags,
+ &assertions, NULL);
+ if (status != REG_OK)
+ {
+ xfree(tags);
+ return status;
+ }
+ node->lastpos =
+ tre_set_union(mem, cat->left->lastpos, cat->right->lastpos,
+ tags, assertions);
+ xfree(tags);
+ if (!node->lastpos)
+ return REG_ESPACE;
+ }
+ else
+ {
+ node->lastpos = cat->right->lastpos;
+ }
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ return REG_OK;
+}
+
+
+/* Adds a transition from each position in `p1' to each position in `p2'. */
+static reg_errcode_t
+tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2,
+ tre_tnfa_transition_t *transitions,
+ int *counts, int *offs)
+{
+ tre_pos_and_tags_t *orig_p2 = p2;
+ tre_tnfa_transition_t *trans;
+ int i, j, k, l, dup, prev_p2_pos;
+
+ if (transitions != NULL)
+ while (p1->position >= 0)
+ {
+ p2 = orig_p2;
+ prev_p2_pos = -1;
+ while (p2->position >= 0)
+ {
+ /* Optimization: if this position was already handled, skip it. */
+ if (p2->position == prev_p2_pos)
+ {
+ p2++;
+ continue;
+ }
+ prev_p2_pos = p2->position;
+ /* Set `trans' to point to the next unused transition from
+ position `p1->position'. */
+ trans = transitions + offs[p1->position];
+ while (trans->state != NULL)
+ {
+#if 0
+ /* If we find a previous transition from `p1->position' to
+ `p2->position', it is overwritten. This can happen only
+ if there are nested loops in the regexp, like in "((a)*)*".
+ In POSIX.2 repetition using the outer loop is always
+ preferred over using the inner loop. Therefore the
+ transition for the inner loop is useless and can be thrown
+ away. */
+ /* XXX - The same position is used for all nodes in a bracket
+ expression, so this optimization cannot be used (it will
+ break bracket expressions) unless I figure out a way to
+ detect it here. */
+ if (trans->state_id == p2->position)
+ {
+ break;
+ }
+#endif
+ trans++;
+ }
+
+ if (trans->state == NULL)
+ (trans + 1)->state = NULL;
+ /* Use the character ranges, assertions, etc. from `p1' for
+ the transition from `p1' to `p2'. */
+ trans->code_min = p1->code_min;
+ trans->code_max = p1->code_max;
+ trans->state = transitions + offs[p2->position];
+ trans->state_id = p2->position;
+ trans->assertions = p1->assertions | p2->assertions
+ | (p1->class ? ASSERT_CHAR_CLASS : 0)
+ | (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0);
+ if (p1->backref >= 0)
+ {
+ assert((trans->assertions & ASSERT_CHAR_CLASS) == 0);
+ assert(p2->backref < 0);
+ trans->u.backref = p1->backref;
+ trans->assertions |= ASSERT_BACKREF;
+ }
+ else
+ trans->u.class = p1->class;
+ if (p1->neg_classes != NULL)
+ {
+ for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++);
+ trans->neg_classes =
+ xmalloc(sizeof(*trans->neg_classes) * (i + 1));
+ if (trans->neg_classes == NULL)
+ return REG_ESPACE;
+ for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++)
+ trans->neg_classes[i] = p1->neg_classes[i];
+ trans->neg_classes[i] = (tre_ctype_t)0;
+ }
+ else
+ trans->neg_classes = NULL;
+
+ /* Find out how many tags this transition has. */
+ i = 0;
+ if (p1->tags != NULL)
+ while(p1->tags[i] >= 0)
+ i++;
+ j = 0;
+ if (p2->tags != NULL)
+ while(p2->tags[j] >= 0)
+ j++;
+
+ /* If we are overwriting a transition, free the old tag array. */
+ if (trans->tags != NULL)
+ xfree(trans->tags);
+ trans->tags = NULL;
+
+ /* If there were any tags, allocate an array and fill it. */
+ if (i + j > 0)
+ {
+ trans->tags = xmalloc(sizeof(*trans->tags) * (i + j + 1));
+ if (!trans->tags)
+ return REG_ESPACE;
+ i = 0;
+ if (p1->tags != NULL)
+ while(p1->tags[i] >= 0)
+ {
+ trans->tags[i] = p1->tags[i];
+ i++;
+ }
+ l = i;
+ j = 0;
+ if (p2->tags != NULL)
+ while (p2->tags[j] >= 0)
+ {
+ /* Don't add duplicates. */
+ dup = 0;
+ for (k = 0; k < i; k++)
+ if (trans->tags[k] == p2->tags[j])
+ {
+ dup = 1;
+ break;
+ }
+ if (!dup)
+ trans->tags[l++] = p2->tags[j];
+ j++;
+ }
+ trans->tags[l] = -1;
+ }
+
+ p2++;
+ }
+ p1++;
+ }
+ else
+ /* Compute a maximum limit for the number of transitions leaving
+ from each state. */
+ while (p1->position >= 0)
+ {
+ p2 = orig_p2;
+ while (p2->position >= 0)
+ {
+ counts[p1->position]++;
+ p2++;
+ }
+ p1++;
+ }
+ return REG_OK;
+}
+
+/* Converts the syntax tree to a TNFA. All the transitions in the TNFA are
+ labelled with one character range (there are no transitions on empty
+ strings). The TNFA takes O(n^2) space in the worst case, `n' is size of
+ the regexp. */
+static reg_errcode_t
+tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions,
+ int *counts, int *offs)
+{
+ tre_union_t *uni;
+ tre_catenation_t *cat;
+ tre_iteration_t *iter;
+ reg_errcode_t errcode = REG_OK;
+
+ /* XXX - recurse using a stack!. */
+ switch (node->type)
+ {
+ case LITERAL:
+ break;
+ case UNION:
+ uni = (tre_union_t *)node->obj;
+ errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs);
+ break;
+
+ case CATENATION:
+ cat = (tre_catenation_t *)node->obj;
+ /* Add a transition from each position in cat->left->lastpos
+ to each position in cat->right->firstpos. */
+ errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos,
+ transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs);
+ break;
+
+ case ITERATION:
+ iter = (tre_iteration_t *)node->obj;
+ assert(iter->max == -1 || iter->max == 1);
+
+ if (iter->max == -1)
+ {
+ assert(iter->min == 0 || iter->min == 1);
+ /* Add a transition from each last position in the iterated
+ expression to each first position. */
+ errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos,
+ transitions, counts, offs);
+ if (errcode != REG_OK)
+ return errcode;
+ }
+ errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs);
+ break;
+ }
+ return errcode;
+}
+
+
+#define ERROR_EXIT(err) \
+ do \
+ { \
+ errcode = err; \
+ if (/*CONSTCOND*/1) \
+ goto error_exit; \
+ } \
+ while (/*CONSTCOND*/0)
+
+
+int
+regcomp(regex_t *restrict preg, const char *restrict regex, int cflags)
+{
+ tre_stack_t *stack;
+ tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r;
+ tre_pos_and_tags_t *p;
+ int *counts = NULL, *offs = NULL;
+ int i, add = 0;
+ tre_tnfa_transition_t *transitions, *initial;
+ tre_tnfa_t *tnfa = NULL;
+ tre_submatch_data_t *submatch_data;
+ tre_tag_direction_t *tag_directions = NULL;
+ reg_errcode_t errcode;
+ tre_mem_t mem;
+
+ /* Parse context. */
+ tre_parse_ctx_t parse_ctx;
+
+ /* Allocate a stack used throughout the compilation process for various
+ purposes. */
+ stack = tre_stack_new(512, 1024000, 128);
+ if (!stack)
+ return REG_ESPACE;
+ /* Allocate a fast memory allocator. */
+ mem = tre_mem_new();
+ if (!mem)
+ {
+ tre_stack_destroy(stack);
+ return REG_ESPACE;
+ }
+
+ /* Parse the regexp. */
+ memset(&parse_ctx, 0, sizeof(parse_ctx));
+ parse_ctx.mem = mem;
+ parse_ctx.stack = stack;
+ parse_ctx.start = regex;
+ parse_ctx.cflags = cflags;
+ parse_ctx.max_backref = -1;
+ errcode = tre_parse(&parse_ctx);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+ preg->re_nsub = parse_ctx.submatch_id - 1;
+ tree = parse_ctx.n;
+
+#ifdef TRE_DEBUG
+ tre_ast_print(tree);
+#endif /* TRE_DEBUG */
+
+ /* Referring to nonexistent subexpressions is illegal. */
+ if (parse_ctx.max_backref > (int)preg->re_nsub)
+ ERROR_EXIT(REG_ESUBREG);
+
+ /* Allocate the TNFA struct. */
+ tnfa = xcalloc(1, sizeof(tre_tnfa_t));
+ if (tnfa == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->have_backrefs = parse_ctx.max_backref >= 0;
+ tnfa->have_approx = 0;
+ tnfa->num_submatches = parse_ctx.submatch_id;
+
+ /* Set up tags for submatch addressing. If REG_NOSUB is set and the
+ regexp does not have back references, this can be skipped. */
+ if (tnfa->have_backrefs || !(cflags & REG_NOSUB))
+ {
+
+ /* Figure out how many tags we will need. */
+ errcode = tre_add_tags(NULL, stack, tree, tnfa);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ if (tnfa->num_tags > 0)
+ {
+ tag_directions = xmalloc(sizeof(*tag_directions)
+ * (tnfa->num_tags + 1));
+ if (tag_directions == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->tag_directions = tag_directions;
+ memset(tag_directions, -1,
+ sizeof(*tag_directions) * (tnfa->num_tags + 1));
+ }
+ tnfa->minimal_tags = xcalloc((unsigned)tnfa->num_tags * 2 + 1,
+ sizeof(*tnfa->minimal_tags));
+ if (tnfa->minimal_tags == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ submatch_data = xcalloc((unsigned)parse_ctx.submatch_id,
+ sizeof(*submatch_data));
+ if (submatch_data == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->submatch_data = submatch_data;
+
+ errcode = tre_add_tags(mem, stack, tree, tnfa);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ }
+
+ /* Expand iteration nodes. */
+ errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position,
+ tag_directions);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ /* Add a dummy node for the final state.
+ XXX - For certain patterns this dummy node can be optimized away,
+ for example "a*" or "ab*". Figure out a simple way to detect
+ this possibility. */
+ tmp_ast_l = tree;
+ tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++);
+ if (tmp_ast_r == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r);
+ if (tree == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ errcode = tre_compute_nfl(mem, stack, tree);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ counts = xmalloc(sizeof(int) * parse_ctx.position);
+ if (counts == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ offs = xmalloc(sizeof(int) * parse_ctx.position);
+ if (offs == NULL)
+ ERROR_EXIT(REG_ESPACE);
+
+ for (i = 0; i < parse_ctx.position; i++)
+ counts[i] = 0;
+ tre_ast_to_tnfa(tree, NULL, counts, NULL);
+
+ add = 0;
+ for (i = 0; i < parse_ctx.position; i++)
+ {
+ offs[i] = add;
+ add += counts[i] + 1;
+ counts[i] = 0;
+ }
+ transitions = xcalloc((unsigned)add + 1, sizeof(*transitions));
+ if (transitions == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->transitions = transitions;
+ tnfa->num_transitions = add;
+
+ errcode = tre_ast_to_tnfa(tree, transitions, counts, offs);
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+
+ tnfa->firstpos_chars = NULL;
+
+ p = tree->firstpos;
+ i = 0;
+ while (p->position >= 0)
+ {
+ i++;
+ p++;
+ }
+
+ initial = xcalloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t));
+ if (initial == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->initial = initial;
+
+ i = 0;
+ for (p = tree->firstpos; p->position >= 0; p++)
+ {
+ initial[i].state = transitions + offs[p->position];
+ initial[i].state_id = p->position;
+ initial[i].tags = NULL;
+ /* Copy the arrays p->tags, and p->params, they are allocated
+ from a tre_mem object. */
+ if (p->tags)
+ {
+ int j;
+ for (j = 0; p->tags[j] >= 0; j++);
+ initial[i].tags = xmalloc(sizeof(*p->tags) * (j + 1));
+ if (!initial[i].tags)
+ ERROR_EXIT(REG_ESPACE);
+ memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1));
+ }
+ initial[i].assertions = p->assertions;
+ i++;
+ }
+ initial[i].state = NULL;
+
+ tnfa->num_transitions = add;
+ tnfa->final = transitions + offs[tree->lastpos[0].position];
+ tnfa->num_states = parse_ctx.position;
+ tnfa->cflags = cflags;
+
+ tre_mem_destroy(mem);
+ tre_stack_destroy(stack);
+ xfree(counts);
+ xfree(offs);
+
+ preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+ return REG_OK;
+
+ error_exit:
+ /* Free everything that was allocated and return the error code. */
+ tre_mem_destroy(mem);
+ if (stack != NULL)
+ tre_stack_destroy(stack);
+ if (counts != NULL)
+ xfree(counts);
+ if (offs != NULL)
+ xfree(offs);
+ preg->TRE_REGEX_T_FIELD = (void *)tnfa;
+ regfree(preg);
+ return errcode;
+}
+
+
+
+
+void
+regfree(regex_t *preg)
+{
+ tre_tnfa_t *tnfa;
+ unsigned int i;
+ tre_tnfa_transition_t *trans;
+
+ tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+ if (!tnfa)
+ return;
+
+ for (i = 0; i < tnfa->num_transitions; i++)
+ if (tnfa->transitions[i].state)
+ {
+ if (tnfa->transitions[i].tags)
+ xfree(tnfa->transitions[i].tags);
+ if (tnfa->transitions[i].neg_classes)
+ xfree(tnfa->transitions[i].neg_classes);
+ }
+ if (tnfa->transitions)
+ xfree(tnfa->transitions);
+
+ if (tnfa->initial)
+ {
+ for (trans = tnfa->initial; trans->state; trans++)
+ {
+ if (trans->tags)
+ xfree(trans->tags);
+ }
+ xfree(tnfa->initial);
+ }
+
+ if (tnfa->submatch_data)
+ {
+ for (i = 0; i < tnfa->num_submatches; i++)
+ if (tnfa->submatch_data[i].parents)
+ xfree(tnfa->submatch_data[i].parents);
+ xfree(tnfa->submatch_data);
+ }
+
+ if (tnfa->tag_directions)
+ xfree(tnfa->tag_directions);
+ if (tnfa->firstpos_chars)
+ xfree(tnfa->firstpos_chars);
+ if (tnfa->minimal_tags)
+ xfree(tnfa->minimal_tags);
+ xfree(tnfa);
+} \ No newline at end of file
diff --git a/lib/mlibc/options/posix/musl-generic-regex/regerror.c b/lib/mlibc/options/posix/musl-generic-regex/regerror.c
new file mode 100644
index 0000000..41e9a36
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/regerror.c
@@ -0,0 +1,37 @@
+#include <string.h>
+#include <regex.h>
+#include <stdio.h>
+// #include "locale_impl.h"
+
+/* Error message strings for error codes listed in `regex.h'. This list
+ needs to be in sync with the codes listed there, naturally. */
+
+/* Converted to single string by Rich Felker to remove the need for
+ * data relocations at runtime, 27 Feb 2006. */
+
+static const char messages[] = {
+ "No error\0"
+ "No match\0"
+ "Invalid regexp\0"
+ "Unknown collating element\0"
+ "Unknown character class name\0"
+ "Trailing backslash\0"
+ "Invalid back reference\0"
+ "Missing ']'\0"
+ "Missing ')'\0"
+ "Missing '}'\0"
+ "Invalid contents of {}\0"
+ "Invalid character range\0"
+ "Out of memory\0"
+ "Repetition not preceded by valid expression\0"
+ "\0Unknown error"
+};
+
+size_t regerror(int e, const regex_t *restrict preg, char *restrict buf, size_t size)
+{
+ const char *s;
+ for (s=messages; e && *s; e--, s+=strlen(s)+1);
+ if (!*s) s++;
+ // s = LCTRANS_CUR(s);
+ return 1+snprintf(buf, size, "%s", s);
+}
diff --git a/lib/mlibc/options/posix/musl-generic-regex/regexec.c b/lib/mlibc/options/posix/musl-generic-regex/regexec.c
new file mode 100644
index 0000000..1a169ab
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/regexec.c
@@ -0,0 +1,1028 @@
+/*
+ regexec.c - TRE POSIX compatible matching functions (and more).
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <regex.h>
+
+#include "tre.h"
+
+#include <assert.h>
+
+static void
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+ const tre_tnfa_t *tnfa, regoff_t *tags, regoff_t match_eo);
+
+/***********************************************************************
+ from tre-match-utils.h
+***********************************************************************/
+
+#define GET_NEXT_WCHAR() do { \
+ prev_c = next_c; pos += pos_add_next; \
+ if ((pos_add_next = mbtowc(&next_c, str_byte, MB_LEN_MAX)) <= 0) { \
+ if (pos_add_next < 0) { ret = REG_NOMATCH; goto error_exit; } \
+ else pos_add_next++; \
+ } \
+ str_byte += pos_add_next; \
+ } while (0)
+
+#define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum(c))
+
+#define CHECK_ASSERTIONS(assertions) \
+ (((assertions & ASSERT_AT_BOL) \
+ && (pos > 0 || reg_notbol) \
+ && (prev_c != L'\n' || !reg_newline)) \
+ || ((assertions & ASSERT_AT_EOL) \
+ && (next_c != L'\0' || reg_noteol) \
+ && (next_c != L'\n' || !reg_newline)) \
+ || ((assertions & ASSERT_AT_BOW) \
+ && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_EOW) \
+ && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_WB) \
+ && (pos != 0 && next_c != L'\0' \
+ && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_WB_NEG) \
+ && (pos == 0 || next_c == L'\0' \
+ || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c))))
+
+#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \
+ (((trans_i->assertions & ASSERT_CHAR_CLASS) \
+ && !(tnfa->cflags & REG_ICASE) \
+ && !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) \
+ || ((trans_i->assertions & ASSERT_CHAR_CLASS) \
+ && (tnfa->cflags & REG_ICASE) \
+ && !tre_isctype(tre_tolower((tre_cint_t)prev_c),trans_i->u.class) \
+ && !tre_isctype(tre_toupper((tre_cint_t)prev_c),trans_i->u.class)) \
+ || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) \
+ && tre_neg_char_classes_match(trans_i->neg_classes,(tre_cint_t)prev_c,\
+ tnfa->cflags & REG_ICASE)))
+
+
+
+
+/* Returns 1 if `t1' wins `t2', 0 otherwise. */
+static int
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+ regoff_t *t1, regoff_t *t2)
+{
+ int i;
+ for (i = 0; i < num_tags; i++)
+ {
+ if (tag_directions[i] == TRE_TAG_MINIMIZE)
+ {
+ if (t1[i] < t2[i])
+ return 1;
+ if (t1[i] > t2[i])
+ return 0;
+ }
+ else
+ {
+ if (t1[i] > t2[i])
+ return 1;
+ if (t1[i] < t2[i])
+ return 0;
+ }
+ }
+ /* assert(0);*/
+ return 0;
+}
+
+static int
+tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase)
+{
+ while (*classes != (tre_ctype_t)0)
+ if ((!icase && tre_isctype(wc, *classes))
+ || (icase && (tre_isctype(tre_toupper(wc), *classes)
+ || tre_isctype(tre_tolower(wc), *classes))))
+ return 1; /* Match. */
+ else
+ classes++;
+ return 0; /* No match. */
+}
+
+
+/***********************************************************************
+ from tre-match-parallel.c
+***********************************************************************/
+
+/*
+ This algorithm searches for matches basically by reading characters
+ in the searched string one by one, starting at the beginning. All
+ matching paths in the TNFA are traversed in parallel. When two or
+ more paths reach the same state, exactly one is chosen according to
+ tag ordering rules; if returning submatches is not required it does
+ not matter which path is chosen.
+
+ The worst case time required for finding the leftmost and longest
+ match, or determining that there is no match, is always linearly
+ dependent on the length of the text being searched.
+
+ This algorithm cannot handle TNFAs with back referencing nodes.
+ See `tre-match-backtrack.c'.
+*/
+
+typedef struct {
+ tre_tnfa_transition_t *state;
+ regoff_t *tags;
+} tre_tnfa_reach_t;
+
+typedef struct {
+ regoff_t pos;
+ regoff_t **tags;
+} tre_reach_pos_t;
+
+
+static reg_errcode_t
+tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string,
+ regoff_t *match_tags, int eflags,
+ regoff_t *match_end_ofs)
+{
+ /* State variables required by GET_NEXT_WCHAR. */
+ tre_char_t prev_c = 0, next_c = 0;
+ const char *str_byte = string;
+ regoff_t pos = -1;
+ regoff_t pos_add_next = 1;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+ int reg_notbol = eflags & REG_NOTBOL;
+ int reg_noteol = eflags & REG_NOTEOL;
+ int reg_newline = tnfa->cflags & REG_NEWLINE;
+ reg_errcode_t ret;
+
+ char *buf;
+ tre_tnfa_transition_t *trans_i;
+ tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i;
+ tre_reach_pos_t *reach_pos;
+ int *tag_i;
+ int num_tags, i;
+
+ regoff_t match_eo = -1; /* end offset of match (-1 if no match found yet) */
+ int new_match = 0;
+ regoff_t *tmp_tags = NULL;
+ regoff_t *tmp_iptr;
+
+#ifdef TRE_MBSTATE
+ memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+ if (!match_tags)
+ num_tags = 0;
+ else
+ num_tags = tnfa->num_tags;
+
+ /* Allocate memory for temporary data required for matching. This needs to
+ be done for every matching operation to be thread safe. This allocates
+ everything in a single large block with calloc(). */
+ {
+ size_t tbytes, rbytes, pbytes, xbytes, total_bytes;
+ char *tmp_buf;
+
+ /* Ensure that tbytes and xbytes*num_states cannot overflow, and that
+ * they don't contribute more than 1/8 of SIZE_MAX to total_bytes. */
+ if (num_tags > SIZE_MAX/(8 * sizeof(regoff_t) * tnfa->num_states))
+ return REG_ESPACE;
+
+ /* Likewise check rbytes. */
+ if (tnfa->num_states+1 > SIZE_MAX/(8 * sizeof(*reach_next)))
+ return REG_ESPACE;
+
+ /* Likewise check pbytes. */
+ if (tnfa->num_states > SIZE_MAX/(8 * sizeof(*reach_pos)))
+ return REG_ESPACE;
+
+ /* Compute the length of the block we need. */
+ tbytes = sizeof(*tmp_tags) * num_tags;
+ rbytes = sizeof(*reach_next) * (tnfa->num_states + 1);
+ pbytes = sizeof(*reach_pos) * tnfa->num_states;
+ xbytes = sizeof(regoff_t) * num_tags;
+ total_bytes =
+ (sizeof(long) - 1) * 4 /* for alignment paddings */
+ + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes;
+
+ /* Allocate the memory. */
+ buf = calloc(total_bytes, 1);
+ if (buf == NULL)
+ return REG_ESPACE;
+
+ /* Get the various pointers within tmp_buf (properly aligned). */
+ tmp_tags = (void *)buf;
+ tmp_buf = buf + tbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ reach_next = (void *)tmp_buf;
+ tmp_buf += rbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ reach = (void *)tmp_buf;
+ tmp_buf += rbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ reach_pos = (void *)tmp_buf;
+ tmp_buf += pbytes;
+ tmp_buf += ALIGN(tmp_buf, long);
+ for (i = 0; i < tnfa->num_states; i++)
+ {
+ reach[i].tags = (void *)tmp_buf;
+ tmp_buf += xbytes;
+ reach_next[i].tags = (void *)tmp_buf;
+ tmp_buf += xbytes;
+ }
+ }
+
+ for (i = 0; i < tnfa->num_states; i++)
+ reach_pos[i].pos = -1;
+
+ GET_NEXT_WCHAR();
+ pos = 0;
+
+ reach_next_i = reach_next;
+ while (1)
+ {
+ /* If no match found yet, add the initial states to `reach_next'. */
+ if (match_eo < 0)
+ {
+ trans_i = tnfa->initial;
+ while (trans_i->state != NULL)
+ {
+ if (reach_pos[trans_i->state_id].pos < pos)
+ {
+ if (trans_i->assertions
+ && CHECK_ASSERTIONS(trans_i->assertions))
+ {
+ trans_i++;
+ continue;
+ }
+
+ reach_next_i->state = trans_i->state;
+ for (i = 0; i < num_tags; i++)
+ reach_next_i->tags[i] = -1;
+ tag_i = trans_i->tags;
+ if (tag_i)
+ while (*tag_i >= 0)
+ {
+ if (*tag_i < num_tags)
+ reach_next_i->tags[*tag_i] = pos;
+ tag_i++;
+ }
+ if (reach_next_i->state == tnfa->final)
+ {
+ match_eo = pos;
+ new_match = 1;
+ for (i = 0; i < num_tags; i++)
+ match_tags[i] = reach_next_i->tags[i];
+ }
+ reach_pos[trans_i->state_id].pos = pos;
+ reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+ reach_next_i++;
+ }
+ trans_i++;
+ }
+ reach_next_i->state = NULL;
+ }
+ else
+ {
+ if (num_tags == 0 || reach_next_i == reach_next)
+ /* We have found a match. */
+ break;
+ }
+
+ /* Check for end of string. */
+ if (!next_c) break;
+
+ GET_NEXT_WCHAR();
+
+ /* Swap `reach' and `reach_next'. */
+ reach_i = reach;
+ reach = reach_next;
+ reach_next = reach_i;
+
+ /* For each state in `reach', weed out states that don't fulfill the
+ minimal matching conditions. */
+ if (tnfa->num_minimals && new_match)
+ {
+ new_match = 0;
+ reach_next_i = reach_next;
+ for (reach_i = reach; reach_i->state; reach_i++)
+ {
+ int skip = 0;
+ for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
+ {
+ int end = tnfa->minimal_tags[i];
+ int start = tnfa->minimal_tags[i + 1];
+ if (end >= num_tags)
+ {
+ skip = 1;
+ break;
+ }
+ else if (reach_i->tags[start] == match_tags[start]
+ && reach_i->tags[end] < match_tags[end])
+ {
+ skip = 1;
+ break;
+ }
+ }
+ if (!skip)
+ {
+ reach_next_i->state = reach_i->state;
+ tmp_iptr = reach_next_i->tags;
+ reach_next_i->tags = reach_i->tags;
+ reach_i->tags = tmp_iptr;
+ reach_next_i++;
+ }
+ }
+ reach_next_i->state = NULL;
+
+ /* Swap `reach' and `reach_next'. */
+ reach_i = reach;
+ reach = reach_next;
+ reach_next = reach_i;
+ }
+
+ /* For each state in `reach' see if there is a transition leaving with
+ the current input symbol to a state not yet in `reach_next', and
+ add the destination states to `reach_next'. */
+ reach_next_i = reach_next;
+ for (reach_i = reach; reach_i->state; reach_i++)
+ {
+ for (trans_i = reach_i->state; trans_i->state; trans_i++)
+ {
+ /* Does this transition match the input symbol? */
+ if (trans_i->code_min <= (tre_cint_t)prev_c &&
+ trans_i->code_max >= (tre_cint_t)prev_c)
+ {
+ if (trans_i->assertions
+ && (CHECK_ASSERTIONS(trans_i->assertions)
+ || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+ {
+ continue;
+ }
+
+ /* Compute the tags after this transition. */
+ for (i = 0; i < num_tags; i++)
+ tmp_tags[i] = reach_i->tags[i];
+ tag_i = trans_i->tags;
+ if (tag_i != NULL)
+ while (*tag_i >= 0)
+ {
+ if (*tag_i < num_tags)
+ tmp_tags[*tag_i] = pos;
+ tag_i++;
+ }
+
+ if (reach_pos[trans_i->state_id].pos < pos)
+ {
+ /* Found an unvisited node. */
+ reach_next_i->state = trans_i->state;
+ tmp_iptr = reach_next_i->tags;
+ reach_next_i->tags = tmp_tags;
+ tmp_tags = tmp_iptr;
+ reach_pos[trans_i->state_id].pos = pos;
+ reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
+
+ if (reach_next_i->state == tnfa->final
+ && (match_eo == -1
+ || (num_tags > 0
+ && reach_next_i->tags[0] <= match_tags[0])))
+ {
+ match_eo = pos;
+ new_match = 1;
+ for (i = 0; i < num_tags; i++)
+ match_tags[i] = reach_next_i->tags[i];
+ }
+ reach_next_i++;
+
+ }
+ else
+ {
+ assert(reach_pos[trans_i->state_id].pos == pos);
+ /* Another path has also reached this state. We choose
+ the winner by examining the tag values for both
+ paths. */
+ if (tre_tag_order(num_tags, tnfa->tag_directions,
+ tmp_tags,
+ *reach_pos[trans_i->state_id].tags))
+ {
+ /* The new path wins. */
+ tmp_iptr = *reach_pos[trans_i->state_id].tags;
+ *reach_pos[trans_i->state_id].tags = tmp_tags;
+ if (trans_i->state == tnfa->final)
+ {
+ match_eo = pos;
+ new_match = 1;
+ for (i = 0; i < num_tags; i++)
+ match_tags[i] = tmp_tags[i];
+ }
+ tmp_tags = tmp_iptr;
+ }
+ }
+ }
+ }
+ }
+ reach_next_i->state = NULL;
+ }
+
+ *match_end_ofs = match_eo;
+ ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
+error_exit:
+ xfree(buf);
+ return ret;
+}
+
+
+
+/***********************************************************************
+ from tre-match-backtrack.c
+***********************************************************************/
+
+/*
+ This matcher is for regexps that use back referencing. Regexp matching
+ with back referencing is an NP-complete problem on the number of back
+ references. The easiest way to match them is to use a backtracking
+ routine which basically goes through all possible paths in the TNFA
+ and chooses the one which results in the best (leftmost and longest)
+ match. This can be spectacularly expensive and may run out of stack
+ space, but there really is no better known generic algorithm. Quoting
+ Henry Spencer from comp.compilers:
+ <URL: http://compilers.iecc.com/comparch/article/93-03-102>
+
+ POSIX.2 REs require longest match, which is really exciting to
+ implement since the obsolete ("basic") variant also includes
+ \<digit>. I haven't found a better way of tackling this than doing
+ a preliminary match using a DFA (or simulation) on a modified RE
+ that just replicates subREs for \<digit>, and then doing a
+ backtracking match to determine whether the subRE matches were
+ right. This can be rather slow, but I console myself with the
+ thought that people who use \<digit> deserve very slow execution.
+ (Pun unintentional but very appropriate.)
+
+*/
+
+typedef struct {
+ regoff_t pos;
+ const char *str_byte;
+ tre_tnfa_transition_t *state;
+ int state_id;
+ int next_c;
+ regoff_t *tags;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+} tre_backtrack_item_t;
+
+typedef struct tre_backtrack_struct {
+ tre_backtrack_item_t item;
+ struct tre_backtrack_struct *prev;
+ struct tre_backtrack_struct *next;
+} *tre_backtrack_t;
+
+#ifdef TRE_MBSTATE
+#define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate)
+#define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
+#else /* !TRE_MBSTATE */
+#define BT_STACK_MBSTATE_IN
+#define BT_STACK_MBSTATE_OUT
+#endif /* !TRE_MBSTATE */
+
+#define tre_bt_mem_new tre_mem_new
+#define tre_bt_mem_alloc tre_mem_alloc
+#define tre_bt_mem_destroy tre_mem_destroy
+
+
+#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
+ do \
+ { \
+ int i; \
+ if (!stack->next) \
+ { \
+ tre_backtrack_t s; \
+ s = tre_bt_mem_alloc(mem, sizeof(*s)); \
+ if (!s) \
+ { \
+ tre_bt_mem_destroy(mem); \
+ if (tags) \
+ xfree(tags); \
+ if (pmatch) \
+ xfree(pmatch); \
+ if (states_seen) \
+ xfree(states_seen); \
+ return REG_ESPACE; \
+ } \
+ s->prev = stack; \
+ s->next = NULL; \
+ s->item.tags = tre_bt_mem_alloc(mem, \
+ sizeof(*tags) * tnfa->num_tags); \
+ if (!s->item.tags) \
+ { \
+ tre_bt_mem_destroy(mem); \
+ if (tags) \
+ xfree(tags); \
+ if (pmatch) \
+ xfree(pmatch); \
+ if (states_seen) \
+ xfree(states_seen); \
+ return REG_ESPACE; \
+ } \
+ stack->next = s; \
+ stack = s; \
+ } \
+ else \
+ stack = stack->next; \
+ stack->item.pos = (_pos); \
+ stack->item.str_byte = (_str_byte); \
+ stack->item.state = (_state); \
+ stack->item.state_id = (_state_id); \
+ stack->item.next_c = (_next_c); \
+ for (i = 0; i < tnfa->num_tags; i++) \
+ stack->item.tags[i] = (_tags)[i]; \
+ BT_STACK_MBSTATE_IN; \
+ } \
+ while (0)
+
+#define BT_STACK_POP() \
+ do \
+ { \
+ int i; \
+ assert(stack->prev); \
+ pos = stack->item.pos; \
+ str_byte = stack->item.str_byte; \
+ state = stack->item.state; \
+ next_c = stack->item.next_c; \
+ for (i = 0; i < tnfa->num_tags; i++) \
+ tags[i] = stack->item.tags[i]; \
+ BT_STACK_MBSTATE_OUT; \
+ stack = stack->prev; \
+ } \
+ while (0)
+
+#undef MIN
+#define MIN(a, b) ((a) <= (b) ? (a) : (b))
+
+static reg_errcode_t
+tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
+ regoff_t *match_tags, int eflags, regoff_t *match_end_ofs)
+{
+ /* State variables required by GET_NEXT_WCHAR. */
+ tre_char_t prev_c = 0, next_c = 0;
+ const char *str_byte = string;
+ regoff_t pos = 0;
+ regoff_t pos_add_next = 1;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate;
+#endif /* TRE_MBSTATE */
+ int reg_notbol = eflags & REG_NOTBOL;
+ int reg_noteol = eflags & REG_NOTEOL;
+ int reg_newline = tnfa->cflags & REG_NEWLINE;
+
+ /* These are used to remember the necessary values of the above
+ variables to return to the position where the current search
+ started from. */
+ int next_c_start;
+ const char *str_byte_start;
+ regoff_t pos_start = -1;
+#ifdef TRE_MBSTATE
+ mbstate_t mbstate_start;
+#endif /* TRE_MBSTATE */
+
+ /* End offset of best match so far, or -1 if no match found yet. */
+ regoff_t match_eo = -1;
+ /* Tag arrays. */
+ int *next_tags;
+ regoff_t *tags = NULL;
+ /* Current TNFA state. */
+ tre_tnfa_transition_t *state;
+ int *states_seen = NULL;
+
+ /* Memory allocator to for allocating the backtracking stack. */
+ tre_mem_t mem = tre_bt_mem_new();
+
+ /* The backtracking stack. */
+ tre_backtrack_t stack;
+
+ tre_tnfa_transition_t *trans_i;
+ regmatch_t *pmatch = NULL;
+ int ret;
+
+#ifdef TRE_MBSTATE
+ memset(&mbstate, '\0', sizeof(mbstate));
+#endif /* TRE_MBSTATE */
+
+ if (!mem)
+ return REG_ESPACE;
+ stack = tre_bt_mem_alloc(mem, sizeof(*stack));
+ if (!stack)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ stack->prev = NULL;
+ stack->next = NULL;
+
+ if (tnfa->num_tags)
+ {
+ tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+ if (!tags)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ }
+ if (tnfa->num_submatches)
+ {
+ pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches);
+ if (!pmatch)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ }
+ if (tnfa->num_states)
+ {
+ states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states);
+ if (!states_seen)
+ {
+ ret = REG_ESPACE;
+ goto error_exit;
+ }
+ }
+
+ retry:
+ {
+ int i;
+ for (i = 0; i < tnfa->num_tags; i++)
+ {
+ tags[i] = -1;
+ if (match_tags)
+ match_tags[i] = -1;
+ }
+ for (i = 0; i < tnfa->num_states; i++)
+ states_seen[i] = 0;
+ }
+
+ state = NULL;
+ pos = pos_start;
+ GET_NEXT_WCHAR();
+ pos_start = pos;
+ next_c_start = next_c;
+ str_byte_start = str_byte;
+#ifdef TRE_MBSTATE
+ mbstate_start = mbstate;
+#endif /* TRE_MBSTATE */
+
+ /* Handle initial states. */
+ next_tags = NULL;
+ for (trans_i = tnfa->initial; trans_i->state; trans_i++)
+ {
+ if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
+ {
+ continue;
+ }
+ if (state == NULL)
+ {
+ /* Start from this state. */
+ state = trans_i->state;
+ next_tags = trans_i->tags;
+ }
+ else
+ {
+ /* Backtrack to this state. */
+ BT_STACK_PUSH(pos, str_byte, 0, trans_i->state,
+ trans_i->state_id, next_c, tags, mbstate);
+ {
+ int *tmp = trans_i->tags;
+ if (tmp)
+ while (*tmp >= 0)
+ stack->item.tags[*tmp++] = pos;
+ }
+ }
+ }
+
+ if (next_tags)
+ for (; *next_tags >= 0; next_tags++)
+ tags[*next_tags] = pos;
+
+
+ if (state == NULL)
+ goto backtrack;
+
+ while (1)
+ {
+ tre_tnfa_transition_t *next_state;
+ int empty_br_match;
+
+ if (state == tnfa->final)
+ {
+ if (match_eo < pos
+ || (match_eo == pos
+ && match_tags
+ && tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
+ tags, match_tags)))
+ {
+ int i;
+ /* This match wins the previous match. */
+ match_eo = pos;
+ if (match_tags)
+ for (i = 0; i < tnfa->num_tags; i++)
+ match_tags[i] = tags[i];
+ }
+ /* Our TNFAs never have transitions leaving from the final state,
+ so we jump right to backtracking. */
+ goto backtrack;
+ }
+
+ /* Go to the next character in the input string. */
+ empty_br_match = 0;
+ trans_i = state;
+ if (trans_i->state && trans_i->assertions & ASSERT_BACKREF)
+ {
+ /* This is a back reference state. All transitions leaving from
+ this state have the same back reference "assertion". Instead
+ of reading the next character, we match the back reference. */
+ regoff_t so, eo;
+ int bt = trans_i->u.backref;
+ regoff_t bt_len;
+ int result;
+
+ /* Get the substring we need to match against. Remember to
+ turn off REG_NOSUB temporarily. */
+ tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB,
+ tnfa, tags, pos);
+ so = pmatch[bt].rm_so;
+ eo = pmatch[bt].rm_eo;
+ bt_len = eo - so;
+
+ result = strncmp((const char*)string + so, str_byte - 1,
+ (size_t)bt_len);
+
+ if (result == 0)
+ {
+ /* Back reference matched. Check for infinite loop. */
+ if (bt_len == 0)
+ empty_br_match = 1;
+ if (empty_br_match && states_seen[trans_i->state_id])
+ {
+ goto backtrack;
+ }
+
+ states_seen[trans_i->state_id] = empty_br_match;
+
+ /* Advance in input string and resync `prev_c', `next_c'
+ and pos. */
+ str_byte += bt_len - 1;
+ pos += bt_len - 1;
+ GET_NEXT_WCHAR();
+ }
+ else
+ {
+ goto backtrack;
+ }
+ }
+ else
+ {
+ /* Check for end of string. */
+ if (next_c == L'\0')
+ goto backtrack;
+
+ /* Read the next character. */
+ GET_NEXT_WCHAR();
+ }
+
+ next_state = NULL;
+ for (trans_i = state; trans_i->state; trans_i++)
+ {
+ if (trans_i->code_min <= (tre_cint_t)prev_c
+ && trans_i->code_max >= (tre_cint_t)prev_c)
+ {
+ if (trans_i->assertions
+ && (CHECK_ASSERTIONS(trans_i->assertions)
+ || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
+ {
+ continue;
+ }
+
+ if (next_state == NULL)
+ {
+ /* First matching transition. */
+ next_state = trans_i->state;
+ next_tags = trans_i->tags;
+ }
+ else
+ {
+ /* Second matching transition. We may need to backtrack here
+ to take this transition instead of the first one, so we
+ push this transition in the backtracking stack so we can
+ jump back here if needed. */
+ BT_STACK_PUSH(pos, str_byte, 0, trans_i->state,
+ trans_i->state_id, next_c, tags, mbstate);
+ {
+ int *tmp;
+ for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
+ stack->item.tags[*tmp] = pos;
+ }
+#if 0 /* XXX - it's important not to look at all transitions here to keep
+ the stack small! */
+ break;
+#endif
+ }
+ }
+ }
+
+ if (next_state != NULL)
+ {
+ /* Matching transitions were found. Take the first one. */
+ state = next_state;
+
+ /* Update the tag values. */
+ if (next_tags)
+ while (*next_tags >= 0)
+ tags[*next_tags++] = pos;
+ }
+ else
+ {
+ backtrack:
+ /* A matching transition was not found. Try to backtrack. */
+ if (stack->prev)
+ {
+ if (stack->item.state->assertions & ASSERT_BACKREF)
+ {
+ states_seen[stack->item.state_id] = 0;
+ }
+
+ BT_STACK_POP();
+ }
+ else if (match_eo < 0)
+ {
+ /* Try starting from a later position in the input string. */
+ /* Check for end of string. */
+ if (next_c == L'\0')
+ {
+ break;
+ }
+ next_c = next_c_start;
+#ifdef TRE_MBSTATE
+ mbstate = mbstate_start;
+#endif /* TRE_MBSTATE */
+ str_byte = str_byte_start;
+ goto retry;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
+ *match_end_ofs = match_eo;
+
+ error_exit:
+ tre_bt_mem_destroy(mem);
+#ifndef TRE_USE_ALLOCA
+ if (tags)
+ xfree(tags);
+ if (pmatch)
+ xfree(pmatch);
+ if (states_seen)
+ xfree(states_seen);
+#endif /* !TRE_USE_ALLOCA */
+
+ return ret;
+}
+
+/***********************************************************************
+ from regexec.c
+***********************************************************************/
+
+/* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match
+ endpoint values. */
+static void
+tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+ const tre_tnfa_t *tnfa, regoff_t *tags, regoff_t match_eo)
+{
+ tre_submatch_data_t *submatch_data;
+ unsigned int i, j;
+ int *parents;
+
+ i = 0;
+ if (match_eo >= 0 && !(cflags & REG_NOSUB))
+ {
+ /* Construct submatch offsets from the tags. */
+ submatch_data = tnfa->submatch_data;
+ while (i < tnfa->num_submatches && i < nmatch)
+ {
+ if (submatch_data[i].so_tag == tnfa->end_tag)
+ pmatch[i].rm_so = match_eo;
+ else
+ pmatch[i].rm_so = tags[submatch_data[i].so_tag];
+
+ if (submatch_data[i].eo_tag == tnfa->end_tag)
+ pmatch[i].rm_eo = match_eo;
+ else
+ pmatch[i].rm_eo = tags[submatch_data[i].eo_tag];
+
+ /* If either of the endpoints were not used, this submatch
+ was not part of the match. */
+ if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1)
+ pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+
+ i++;
+ }
+ /* Reset all submatches that are not within all of their parent
+ submatches. */
+ i = 0;
+ while (i < tnfa->num_submatches && i < nmatch)
+ {
+ if (pmatch[i].rm_eo == -1)
+ assert(pmatch[i].rm_so == -1);
+ assert(pmatch[i].rm_so <= pmatch[i].rm_eo);
+
+ parents = submatch_data[i].parents;
+ if (parents != NULL)
+ for (j = 0; parents[j] >= 0; j++)
+ {
+ if (pmatch[i].rm_so < pmatch[parents[j]].rm_so
+ || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo)
+ pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+ }
+ i++;
+ }
+ }
+
+ while (i < nmatch)
+ {
+ pmatch[i].rm_so = -1;
+ pmatch[i].rm_eo = -1;
+ i++;
+ }
+}
+
+
+/*
+ Wrapper functions for POSIX compatible regexp matching.
+*/
+
+int
+regexec(const regex_t *restrict preg, const char *restrict string,
+ size_t nmatch, regmatch_t pmatch[restrict], int eflags)
+{
+ tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
+ reg_errcode_t status;
+ regoff_t *tags = NULL, eo;
+ if (tnfa->cflags & REG_NOSUB) nmatch = 0;
+ if (tnfa->num_tags > 0 && nmatch > 0)
+ {
+ tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
+ if (tags == NULL)
+ return REG_ESPACE;
+ }
+
+ /* Dispatch to the appropriate matcher. */
+ if (tnfa->have_backrefs)
+ {
+ /* The regex has back references, use the backtracking matcher. */
+ status = tre_tnfa_run_backtrack(tnfa, string, tags, eflags, &eo);
+ }
+ else
+ {
+ /* Exact matching, no back references, use the parallel matcher. */
+ status = tre_tnfa_run_parallel(tnfa, string, tags, eflags, &eo);
+ }
+
+ if (status == REG_OK)
+ /* A match was found, so fill the submatch registers. */
+ tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo);
+ if (tags)
+ xfree(tags);
+ return status;
+} \ No newline at end of file
diff --git a/lib/mlibc/options/posix/musl-generic-regex/tre-mem.c b/lib/mlibc/options/posix/musl-generic-regex/tre-mem.c
new file mode 100644
index 0000000..a3df685
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/tre-mem.c
@@ -0,0 +1,158 @@
+/*
+ tre-mem.c - TRE memory allocator
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/*
+ This memory allocator is for allocating small memory blocks efficiently
+ in terms of memory overhead and execution speed. The allocated blocks
+ cannot be freed individually, only all at once. There can be multiple
+ allocators, though.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "tre.h"
+
+/*
+ This memory allocator is for allocating small memory blocks efficiently
+ in terms of memory overhead and execution speed. The allocated blocks
+ cannot be freed individually, only all at once. There can be multiple
+ allocators, though.
+*/
+
+/* Returns a new memory allocator or NULL if out of memory. */
+tre_mem_t
+tre_mem_new_impl(int provided, void *provided_block)
+{
+ tre_mem_t mem;
+ if (provided)
+ {
+ mem = provided_block;
+ memset(mem, 0, sizeof(*mem));
+ }
+ else
+ mem = xcalloc(1, sizeof(*mem));
+ if (mem == NULL)
+ return NULL;
+ return mem;
+}
+
+
+/* Frees the memory allocator and all memory allocated with it. */
+void
+tre_mem_destroy(tre_mem_t mem)
+{
+ tre_list_t *tmp, *l = mem->blocks;
+
+ while (l != NULL)
+ {
+ xfree(l->data);
+ tmp = l->next;
+ xfree(l);
+ l = tmp;
+ }
+ xfree(mem);
+}
+
+
+/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
+ allocated block or NULL if an underlying malloc() failed. */
+void *
+tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+ int zero, size_t size)
+{
+ void *ptr;
+
+ if (mem->failed)
+ {
+ return NULL;
+ }
+
+ if (mem->n < size)
+ {
+ /* We need more memory than is available in the current block.
+ Allocate a new block. */
+ tre_list_t *l;
+ if (provided)
+ {
+ if (provided_block == NULL)
+ {
+ mem->failed = 1;
+ return NULL;
+ }
+ mem->ptr = provided_block;
+ mem->n = TRE_MEM_BLOCK_SIZE;
+ }
+ else
+ {
+ int block_size;
+ if (size * 8 > TRE_MEM_BLOCK_SIZE)
+ block_size = size * 8;
+ else
+ block_size = TRE_MEM_BLOCK_SIZE;
+ l = xmalloc(sizeof(*l));
+ if (l == NULL)
+ {
+ mem->failed = 1;
+ return NULL;
+ }
+ l->data = xmalloc(block_size);
+ if (l->data == NULL)
+ {
+ xfree(l);
+ mem->failed = 1;
+ return NULL;
+ }
+ l->next = NULL;
+ if (mem->current != NULL)
+ mem->current->next = l;
+ if (mem->blocks == NULL)
+ mem->blocks = l;
+ mem->current = l;
+ mem->ptr = l->data;
+ mem->n = block_size;
+ }
+ }
+
+ /* Make sure the next pointer will be aligned. */
+ size += ALIGN(mem->ptr + size, long);
+
+ /* Allocate from current block. */
+ ptr = mem->ptr;
+ mem->ptr += size;
+ mem->n -= size;
+
+ /* Set to zero if needed. */
+ if (zero)
+ memset(ptr, 0, size);
+
+ return ptr;
+} \ No newline at end of file
diff --git a/lib/mlibc/options/posix/musl-generic-regex/tre.h b/lib/mlibc/options/posix/musl-generic-regex/tre.h
new file mode 100644
index 0000000..5891f75
--- /dev/null
+++ b/lib/mlibc/options/posix/musl-generic-regex/tre.h
@@ -0,0 +1,241 @@
+// Taken from musl tre.h
+/*
+ tre-internal.h - TRE internal definitions
+
+ Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <regex.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#define hidden __attribute__((__visibility__("hidden")))
+
+// TODO: These should probably go in limits.h
+#define CHARCLASS_NAME_MAX 14
+#define RE_DUP_MAX 255
+
+#undef TRE_MBSTATE
+
+#define NDEBUG
+
+#define TRE_REGEX_T_FIELD __opaque
+typedef int reg_errcode_t;
+
+typedef wchar_t tre_char_t;
+
+#define DPRINT(msg) do { } while(0)
+
+#define elementsof(x) ( sizeof(x) / sizeof(x[0]) )
+
+#define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n)))
+
+/* Wide characters. */
+typedef wint_t tre_cint_t;
+#define TRE_CHAR_MAX 0x10ffff
+
+#define tre_isalnum iswalnum
+#define tre_isalpha iswalpha
+#define tre_isblank iswblank
+#define tre_iscntrl iswcntrl
+#define tre_isdigit iswdigit
+#define tre_isgraph iswgraph
+#define tre_islower iswlower
+#define tre_isprint iswprint
+#define tre_ispunct iswpunct
+#define tre_isspace iswspace
+#define tre_isupper iswupper
+#define tre_isxdigit iswxdigit
+
+#define tre_tolower towlower
+#define tre_toupper towupper
+#define tre_strlen wcslen
+
+/* Use system provided iswctype() and wctype(). */
+typedef wctype_t tre_ctype_t;
+#define tre_isctype iswctype
+#define tre_ctype wctype
+
+/* Returns number of bytes to add to (char *)ptr to make it
+ properly aligned for the type. */
+#define ALIGN(ptr, type) \
+ ((((long)ptr) % sizeof(type)) \
+ ? (sizeof(type) - (((long)ptr) % sizeof(type))) \
+ : 0)
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+
+/* TNFA transition type. A TNFA state is an array of transitions,
+ the terminator is a transition with NULL `state'. */
+typedef struct tnfa_transition tre_tnfa_transition_t;
+
+struct tnfa_transition {
+ /* Range of accepted characters. */
+ tre_cint_t code_min;
+ tre_cint_t code_max;
+ /* Pointer to the destination state. */
+ tre_tnfa_transition_t *state;
+ /* ID number of the destination state. */
+ int state_id;
+ /* -1 terminated array of tags (or NULL). */
+ int *tags;
+ /* Assertion bitmap. */
+ int assertions;
+ /* Assertion parameters. */
+ union {
+ /* Character class assertion. */
+ tre_ctype_t class;
+ /* Back reference assertion. */
+ int backref;
+ } u;
+ /* Negative character class assertions. */
+ tre_ctype_t *neg_classes;
+};
+
+
+/* Assertions. */
+#define ASSERT_AT_BOL 1 /* Beginning of line. */
+#define ASSERT_AT_EOL 2 /* End of line. */
+#define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */
+#define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */
+#define ASSERT_AT_BOW 16 /* Beginning of word. */
+#define ASSERT_AT_EOW 32 /* End of word. */
+#define ASSERT_AT_WB 64 /* Word boundary. */
+#define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */
+#define ASSERT_BACKREF 256 /* A back reference in `backref'. */
+#define ASSERT_LAST 256
+
+/* Tag directions. */
+typedef enum {
+ TRE_TAG_MINIMIZE = 0,
+ TRE_TAG_MAXIMIZE = 1
+} tre_tag_direction_t;
+
+/* Instructions to compute submatch register values from tag values
+ after a successful match. */
+struct tre_submatch_data {
+ /* Tag that gives the value for rm_so (submatch start offset). */
+ int so_tag;
+ /* Tag that gives the value for rm_eo (submatch end offset). */
+ int eo_tag;
+ /* List of submatches this submatch is contained in. */
+ int *parents;
+};
+
+typedef struct tre_submatch_data tre_submatch_data_t;
+
+
+/* TNFA definition. */
+typedef struct tnfa tre_tnfa_t;
+
+struct tnfa {
+ tre_tnfa_transition_t *transitions;
+ unsigned int num_transitions;
+ tre_tnfa_transition_t *initial;
+ tre_tnfa_transition_t *final;
+ tre_submatch_data_t *submatch_data;
+ char *firstpos_chars;
+ int first_char;
+ unsigned int num_submatches;
+ tre_tag_direction_t *tag_directions;
+ int *minimal_tags;
+ int num_tags;
+ int num_minimals;
+ int end_tag;
+ int num_states;
+ int cflags;
+ int have_backrefs;
+ int have_approx;
+};
+
+/* from tre-mem.h: */
+
+#define TRE_MEM_BLOCK_SIZE 1024
+
+typedef struct tre_list {
+ void *data;
+ struct tre_list *next;
+} tre_list_t;
+
+typedef struct tre_mem_struct {
+ tre_list_t *blocks;
+ tre_list_t *current;
+ char *ptr;
+ size_t n;
+ int failed;
+ void **provided;
+} *tre_mem_t;
+
+#ifndef __MLIBC_ABI_ONLY
+
+#define tre_mem_new_impl __tre_mem_new_impl
+#define tre_mem_alloc_impl __tre_mem_alloc_impl
+#define tre_mem_destroy __tre_mem_destroy
+
+hidden tre_mem_t tre_mem_new_impl(int provided, void *provided_block);
+hidden void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
+ int zero, size_t size);
+
+/* Returns a new memory allocator or NULL if out of memory. */
+#define tre_mem_new() tre_mem_new_impl(0, NULL)
+
+/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
+ allocated block or NULL if an underlying malloc() failed. */
+#define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size)
+
+/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
+ allocated block or NULL if an underlying malloc() failed. The memory
+ is set to zero. */
+#define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size)
+
+#ifdef TRE_USE_ALLOCA
+/* alloca() versions. Like above, but memory is allocated with alloca()
+ instead of malloc(). */
+
+#define tre_mem_newa() \
+ tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct)))
+
+#define tre_mem_alloca(mem, size) \
+ ((mem)->n >= (size) \
+ ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \
+ : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size)))
+#endif /* TRE_USE_ALLOCA */
+
+
+/* Frees the memory allocator and all memory allocated with it. */
+hidden void tre_mem_destroy(tre_mem_t mem);
+
+#define xmalloc malloc
+#define xcalloc calloc
+#define xfree free
+#define xrealloc realloc
+
+#endif /* !__MLIBC_ABI_ONLY */