diff options
Diffstat (limited to 'lib/mlibc/options/posix')
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(¤t_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, ¤t_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, ¤t_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, ¤t_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, ¤t_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, ©, + &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, ©, &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 */ |