summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/glibc/generic
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-03-07 17:28:00 -0500
committerIan Moffett <ian@osmora.org>2024-03-07 17:28:32 -0500
commitbd5969fc876a10b18613302db7087ef3c40f18e1 (patch)
tree7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/options/glibc/generic
parenta95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff)
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/options/glibc/generic')
-rw-r--r--lib/mlibc/options/glibc/generic/err.cpp64
-rw-r--r--lib/mlibc/options/glibc/generic/error.cpp65
-rw-r--r--lib/mlibc/options/glibc/generic/execinfo.cpp17
-rw-r--r--lib/mlibc/options/glibc/generic/getopt-stubs.cpp253
-rw-r--r--lib/mlibc/options/glibc/generic/glibc-assert.cpp14
-rw-r--r--lib/mlibc/options/glibc/generic/glibc-signal.cpp14
-rw-r--r--lib/mlibc/options/glibc/generic/gshadow.cpp7
-rw-r--r--lib/mlibc/options/glibc/generic/malloc.cpp6
-rw-r--r--lib/mlibc/options/glibc/generic/personality.cpp15
-rw-r--r--lib/mlibc/options/glibc/generic/printf.cpp7
-rw-r--r--lib/mlibc/options/glibc/generic/resolv-stubs.cpp36
-rw-r--r--lib/mlibc/options/glibc/generic/shadow-stubs.cpp217
-rw-r--r--lib/mlibc/options/glibc/generic/stdio_ext-stubs.cpp64
-rw-r--r--lib/mlibc/options/glibc/generic/string.cpp32
-rw-r--r--lib/mlibc/options/glibc/generic/sys-io.cpp25
-rw-r--r--lib/mlibc/options/glibc/generic/sys-ioctl.cpp21
-rw-r--r--lib/mlibc/options/glibc/generic/sys-timex.cpp17
17 files changed, 874 insertions, 0 deletions
diff --git a/lib/mlibc/options/glibc/generic/err.cpp b/lib/mlibc/options/glibc/generic/err.cpp
new file mode 100644
index 0000000..042c1d8
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/err.cpp
@@ -0,0 +1,64 @@
+#include <err.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// va_list
+
+void vwarn(const char *fmt, va_list params) {
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ if (fmt) {
+ vfprintf(stderr, fmt, params);
+ fwrite(": ", 1, 2, stderr);
+ }
+ perror(NULL);
+}
+
+void vwarnx(const char *fmt, va_list params) {
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ if (fmt) {
+ vfprintf(stderr, fmt, params);
+ }
+ putc('\n', stderr);
+}
+
+__attribute__((__noreturn__)) void verr(int status, const char *fmt, va_list params) {
+ vwarn(fmt, params);
+ exit(status);
+}
+
+__attribute__((__noreturn__)) void verrx(int status, const char *fmt, va_list params) {
+ vwarnx(fmt, params);
+ exit(status);
+}
+
+// variadic
+
+void warn(const char *fmt, ...) {
+ va_list params;
+ va_start(params, fmt);
+ vwarn(fmt, params);
+ va_end(params);
+}
+
+void warnx(const char *fmt, ...) {
+ va_list params;
+ va_start(params, fmt);
+ vwarnx(fmt, params);
+ va_end(params);
+}
+
+__attribute__((__noreturn__)) void err(int status, const char *fmt, ...) {
+ va_list params;
+ va_start(params, fmt);
+ verr(status, fmt, params);
+ va_end(params);
+}
+
+__attribute__((__noreturn__)) void errx(int status, const char *fmt, ...) {
+ va_list params;
+ va_start(params, fmt);
+ verrx(status, fmt, params);
+ va_end(params);
+}
diff --git a/lib/mlibc/options/glibc/generic/error.cpp b/lib/mlibc/options/glibc/generic/error.cpp
new file mode 100644
index 0000000..45203b2
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/error.cpp
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <error.h>
+
+unsigned int error_message_count = 0;
+int error_one_per_line = 0;
+void (*error_print_progname)(void) = NULL;
+
+void error(int status, int errnum, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ error_message_count++;
+
+ fflush(stdout);
+ if(error_print_progname) {
+ error_print_progname();
+ } else {
+ fprintf(stderr, "%s: ", program_invocation_name);
+ }
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ if(errnum) {
+ fprintf(stderr, ": %s\n", strerror(errnum));
+ }
+
+ if(status) {
+ exit(status);
+ }
+}
+
+void error_at_line(int status, int errnum, const char *filename, unsigned int linenum, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+
+ static bool first_call = true;
+ static unsigned int last_line = 0;
+ if(!(last_line == linenum && error_one_per_line && !first_call)) {
+ first_call = false;
+ last_line = linenum;
+ error_message_count++;
+
+ fflush(stdout);
+ if(error_print_progname) {
+ error_print_progname();
+ } else {
+ fprintf(stderr, "%s:", program_invocation_name);
+ }
+ fprintf(stderr, "%s:%u: ", filename, linenum);
+ vfprintf(stderr, format, args);
+
+ if(errnum) {
+ fprintf(stderr, ": %s\n", strerror(errnum));
+ }
+ }
+ va_end(args);
+
+ if(status) {
+ exit(status);
+ }
+}
diff --git a/lib/mlibc/options/glibc/generic/execinfo.cpp b/lib/mlibc/options/glibc/generic/execinfo.cpp
new file mode 100644
index 0000000..3474615
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/execinfo.cpp
@@ -0,0 +1,17 @@
+#include <execinfo.h>
+#include <bits/ensure.h>
+
+int backtrace(void **, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+char **backtrace_symbols(void *const *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void backtrace_symbols_fd(void *const *, int, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/glibc/generic/getopt-stubs.cpp b/lib/mlibc/options/glibc/generic/getopt-stubs.cpp
new file mode 100644
index 0000000..d13d4eb
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/getopt-stubs.cpp
@@ -0,0 +1,253 @@
+#include <assert.h>
+#include <bits/ensure.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <frg/optional.hpp>
+#include <mlibc/debug.hpp>
+
+char *optarg;
+int optind = 1;
+int opterr = 1;
+int optopt;
+
+namespace {
+
+int __optpos = 1;
+
+enum GetoptMode {
+ Short,
+ Long,
+ LongOnly,
+};
+
+int getopt_common(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex, enum GetoptMode mode) {
+ auto longopt_consume = [&](const char *arg, char *s, int k, bool colon) -> frg::optional<int> {
+ // Consume the option and its argument.
+ if(longopts[k].has_arg == required_argument) {
+ if(s) {
+ // Consume the long option and its argument.
+ optarg = s + 1;
+ optind++;
+ }else if(argv[optind + 1]) {
+ // Consume the long option.
+ optind++;
+
+ // Consume the option's argument.
+ optarg = argv[optind];
+ optind++;
+ }else{
+ /* If an error was detected, and the first character of optstring is not a colon,
+ and the external variable opterr is nonzero (which is the default),
+ getopt() prints an error message. */
+ if(!colon && opterr)
+ fprintf(stderr, "--%s requires an argument.\n", arg);
+ /* If the first character of optstring is a colon (':'), then getopt()
+ returns ':' instead of '?' to indicate a missing option argument. */
+ return colon ? ':' : '?';
+ }
+ }else if(longopts[k].has_arg == optional_argument) {
+ if(s) {
+ // Consume the long option and its argument.
+ optarg = s + 1;
+ optind++;
+ }else{
+ // Consume the long option.
+ optarg = nullptr;
+ optind++;
+ }
+ }else{
+ __ensure(longopts[k].has_arg == no_argument);
+
+ // Consume the long option.
+ optind++;
+ }
+
+ return frg::null_opt;
+ };
+
+ bool colon = optstring[0] == ':';
+ bool stop_at_first_nonarg = (optstring[0] == '+' || getenv("POSIXLY_CORRECT"));
+
+ // glibc extension: Setting optind to zero causes a full reset.
+ // TODO: Should we really reset opterr and the other flags?
+ if(!optind
+#if __MLIBC_BSD_OPTION
+ || optreset
+#endif //__MLIBC_BSD_OPTION
+ ) {
+ optarg = nullptr;
+ optind = 1;
+ opterr = 1;
+ optopt = 0;
+ __optpos = 1;
+#if __MLIBC_BSD_OPTION
+ optreset = 0;
+#endif //__MLIBC_BSD_OPTION
+ }
+
+ auto isOptionArg = [](char *arg){
+ // If the first character of arg '-', and the arg is not exactly
+ // equal to "-" or "--", then the arg is an option argument.
+ return arg[0] == '-' && strcmp(arg, "-") && strcmp(arg, "--");
+ };
+
+ while(optind < argc) {
+ char *arg = argv[optind];
+ if(!isOptionArg(arg)) {
+ if(stop_at_first_nonarg) {
+ return -1;
+ }
+
+ bool further_options = false;
+ int skip = optind;
+
+ for(; skip < argc; ++skip) {
+ if(isOptionArg(argv[skip])) {
+ further_options = true;
+ break;
+ }
+ }
+
+ if(further_options) {
+ optind += skip - optind;
+ continue;
+ } else {
+ optarg = nullptr;
+ return -1;
+ }
+ }
+
+ if(arg[1] == '-') {
+ arg += 2;
+
+ // Determine the end of the option name (vs. the start of the argument).
+ auto s = strchr(arg, '=');
+ size_t n = s ? (s - arg) : strlen(arg);
+
+ int k = -1;
+ for(int i = 0; longopts[i].name; i++) {
+ if(strncmp(arg, longopts[i].name, n) || longopts[i].name[n])
+ continue;
+
+ if(k >= 0) {
+ if(opterr)
+ fprintf(stderr, "Multiple option declaration detected: %s\n", arg);
+ return '?';
+ }
+ k = i;
+ }
+
+ if(k == -1) {
+ if(opterr)
+ fprintf(stderr, "--%s is not a valid option.\n", arg);
+ return '?';
+ }
+
+ if(longindex)
+ *longindex = k;
+
+ if(auto r = longopt_consume(arg, s, k, colon); r)
+ return r.value();
+
+ if(!longopts[k].flag) {
+ return longopts[k].val;
+ }else{
+ *longopts[k].flag = longopts[k].val;
+ return 0;
+ }
+ }else{
+ /* handle short options, i.e. options with only one dash prefixed; e.g. `program -s` */
+ unsigned int i = __optpos;
+ while(true) {
+ if(mode == GetoptMode::LongOnly) {
+ const char *lo_arg = &arg[1];
+ auto s = strchr(lo_arg, '=');
+ size_t n = s ? (s - lo_arg) : strlen(lo_arg);
+ int k = -1;
+
+ for(int longopt = 0; longopts[longopt].name; longopt++) {
+ if(strncmp(lo_arg, longopts[longopt].name, n) || longopts[longopt].name[n])
+ continue;
+
+ if(k >= 0) {
+ if(opterr)
+ fprintf(stderr, "Multiple option declaration detected: %s\n", arg);
+ return '?';
+ }
+
+ k = longopt;
+ }
+
+ if(k != -1) {
+ if(auto r = longopt_consume(lo_arg, s, k, colon); r)
+ return r.value();
+
+ if(!longopts[k].flag) {
+ return longopts[k].val;
+ }else{
+ *longopts[k].flag = longopts[k].val;
+ return 0;
+ }
+ }
+ }
+
+ auto opt = strchr(optstring, arg[i]);
+ if(opt) {
+ if(opt[1] == ':') {
+ bool required = (opt[2] != ':');
+
+ if(arg[i+1]) {
+ optarg = arg + i + 1;
+ } else if(optind + 1 < argc && argv[optind + 1] && (required || argv[optind + 1][0] != '-')) {
+ /* there is an argument to this short option, separated by a space */
+ optarg = argv[optind + 1];
+ optind++;
+ __optpos = 1;
+ } else if(!required) {
+ optarg = nullptr;
+ } else {
+ __optpos = 1;
+ optopt = arg[i];
+ return colon ? ':' : '?';
+ }
+ optind++;
+ } else {
+ if(arg[i+1]) {
+ __optpos++;
+ } else if(arg[i]) {
+ optind++;
+ } else {
+ return -1;
+ }
+ }
+
+ return arg[i];
+ } else {
+ /* If getopt() does not recognize an option character, it prints an error message to stderr,
+ stores the character in optopt, and returns '?'. The calling program may prevent
+ the error message by setting opterr to 0. */
+ optopt = arg[1];
+ if(opterr)
+ fprintf(stderr, "%s is not a valid option.\n", arg);
+ return '?';
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+}
+
+int getopt_long(int argc, char * const argv[], const char *optstring,
+ const struct option *longopts, int *longindex) {
+ return getopt_common(argc, argv, optstring, longopts, longindex, GetoptMode::Long);
+}
+
+int getopt_long_only(int argc, char * const argv[], const char *optstring,
+ const struct option *longopts, int *longindex) {
+ return getopt_common(argc, argv, optstring, longopts, longindex, GetoptMode::LongOnly);
+}
diff --git a/lib/mlibc/options/glibc/generic/glibc-assert.cpp b/lib/mlibc/options/glibc/generic/glibc-assert.cpp
new file mode 100644
index 0000000..77cd498
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/glibc-assert.cpp
@@ -0,0 +1,14 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <bits/ensure.h>
+
+[[gnu::noreturn]] void __assert_fail_perror(int errno, const char *file, unsigned int line,
+ const char *function) {
+ char *errormsg = strerror(errno);
+ fprintf(stderr, "In function %s, file %s:%d: Errno '%s' failed!\n",
+ function, file, line, errormsg);
+ abort();
+}
diff --git a/lib/mlibc/options/glibc/generic/glibc-signal.cpp b/lib/mlibc/options/glibc/generic/glibc-signal.cpp
new file mode 100644
index 0000000..41bc455
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/glibc-signal.cpp
@@ -0,0 +1,14 @@
+#include <errno.h>
+#include <signal.h>
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/glibc-sysdeps.hpp>
+
+int tgkill(int tgid, int tid, int sig) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_tgkill, -1);
+ if(int e = mlibc::sys_tgkill(tgid, tid, sig); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/mlibc/options/glibc/generic/gshadow.cpp b/lib/mlibc/options/glibc/generic/gshadow.cpp
new file mode 100644
index 0000000..f93a47d
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/gshadow.cpp
@@ -0,0 +1,7 @@
+#include <gshadow.h>
+#include <bits/ensure.h>
+
+int getsgnam_r(const char *, struct sgrp *, char *, size_t, struct sgrp **) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/glibc/generic/malloc.cpp b/lib/mlibc/options/glibc/generic/malloc.cpp
new file mode 100644
index 0000000..b5a4daf
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/malloc.cpp
@@ -0,0 +1,6 @@
+#include <bits/glibc/glibc_malloc.h>
+#include <mlibc/allocator.hpp>
+
+size_t malloc_usable_size(void *p) {
+ return getAllocator().get_size(p);
+}
diff --git a/lib/mlibc/options/glibc/generic/personality.cpp b/lib/mlibc/options/glibc/generic/personality.cpp
new file mode 100644
index 0000000..3bfd9aa
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/personality.cpp
@@ -0,0 +1,15 @@
+#include <bits/ensure.h>
+#include <errno.h>
+#include <mlibc/glibc-sysdeps.hpp>
+#include <sys/personality.h>
+
+int personality(unsigned long persona) {
+ int out = 0;
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_personality, -1);
+
+ if(int e = sysdep(persona, &out); e) {
+ errno = e;
+ return -1;
+ }
+ return out;
+}
diff --git a/lib/mlibc/options/glibc/generic/printf.cpp b/lib/mlibc/options/glibc/generic/printf.cpp
new file mode 100644
index 0000000..4abb00d
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/printf.cpp
@@ -0,0 +1,7 @@
+#include <bits/ensure.h>
+#include <printf.h>
+
+size_t parse_printf_format(const char * __restrict, size_t, int * __restrict) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
diff --git a/lib/mlibc/options/glibc/generic/resolv-stubs.cpp b/lib/mlibc/options/glibc/generic/resolv-stubs.cpp
new file mode 100644
index 0000000..7c0723e
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/resolv-stubs.cpp
@@ -0,0 +1,36 @@
+#include <resolv.h>
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+int dn_expand(const unsigned char *, const unsigned char *,
+ const unsigned char *, char *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int res_query(const char *, int, int, unsigned char *, int) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int res_init() {
+ mlibc::infoLogger() << "mlibc: res_init is a stub!" << frg::endlog;
+ return 0;
+}
+
+int res_ninit(res_state) {
+ mlibc::infoLogger() << "mlibc: res_ninit is a stub!" << frg::endlog;
+ return 0;
+}
+
+void res_nclose(res_state) {
+ mlibc::infoLogger() << "mlibc: res_nclose is a stub!" << frg::endlog;
+ return;
+}
+
+/* This is completely unused, and exists purely to satisfy broken apps. */
+
+struct __res_state *__res_state() {
+ static struct __res_state res;
+ return &res;
+}
diff --git a/lib/mlibc/options/glibc/generic/shadow-stubs.cpp b/lib/mlibc/options/glibc/generic/shadow-stubs.cpp
new file mode 100644
index 0000000..9ce6584
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/shadow-stubs.cpp
@@ -0,0 +1,217 @@
+#include <shadow.h>
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+/*
+ * The code in this file is largely based on or taken from musl.
+ * This includes:
+ * - xatol
+ * - __parsespent
+ * - cleanup
+ * - getspnam_r
+ * - getspnam
+ */
+#define NUM(n) ((n) == -1 ? 0 : -1), ((n) == -1 ? 0 : (n))
+
+int putspent(const struct spwd *sp, FILE *f) {
+ auto str = [] (char *s) {
+ return ((s) ? (s) : "");
+ };
+ return fprintf(f, "%s:%s:%.*d:%.*d:%.*d:%.*d:%.*d:%.*d:%.*u\n",
+ str(sp->sp_namp), str(sp->sp_pwdp), NUM(sp->sp_lstchg),
+ NUM(sp->sp_min), NUM(sp->sp_max), NUM(sp->sp_warn),
+ NUM(sp->sp_inact), NUM(sp->sp_expire), NUM((int)sp->sp_flag)) < 0 ? -1 : 0;
+}
+#undef NUM
+
+static long xatol(char **s) {
+ long x;
+ if(**s == ':' || **s == '\n') {
+ return -1;
+ }
+ for(x = 0; (unsigned int)**s - '0' < 10U; ++*s) {
+ x = 10 * x + (**s - '0');
+ }
+ return x;
+}
+
+static int __parsespent(char *s, struct spwd *sp) {
+ sp->sp_namp = s;
+ if(!(s = strchr(s, ':'))) {
+ return -1;
+ }
+ *s = 0;
+
+ sp->sp_pwdp = ++s;
+ if(!(s = strchr(s, ':'))) {
+ return -1;
+ }
+ *s = 0;
+
+ s++;
+ sp->sp_lstchg = xatol(&s);
+ if(*s != ':') {
+ return -1;
+ }
+
+ s++;
+ sp->sp_min = xatol(&s);
+ if(*s != ':') {
+ return -1;
+ }
+
+ s++;
+ sp->sp_max = xatol(&s);
+ if(*s != ':') {
+ return -1;
+ }
+
+ s++;
+ sp->sp_warn = xatol(&s);
+ if(*s != ':') {
+ return -1;
+ }
+
+ s++;
+ sp->sp_inact = xatol(&s);
+ if(*s != ':') {
+ return -1;
+ }
+
+ s++;
+ sp->sp_expire = xatol(&s);
+ if(*s != ':') {
+ return -1;
+ }
+
+ s++;
+ sp->sp_flag = xatol(&s);
+ if(*s != '\n') {
+ return -1;
+ }
+ return 0;
+}
+
+static void cleanup(void *p) {
+ fclose((FILE *)p);
+}
+
+int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res) {
+ char path[20 + NAME_MAX];
+ FILE *f = 0;
+ int rv = 0;
+ int fd;
+ size_t k, l = strlen(name);
+ int skip = 0;
+ int cs;
+ int orig_errno = errno;
+
+ *res = 0;
+
+ /* Disallow potentially-malicious user names */
+ if(*name=='.' || strchr(name, '/') || !l) {
+ return errno = EINVAL;
+ }
+
+ /* Buffer size must at least be able to hold name, plus some.. */
+ if(size < l + 100) {
+ return errno = ERANGE;
+ }
+
+ /* Protect against truncation */
+ if(snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= (int)sizeof path) {
+ return errno = EINVAL;
+ }
+
+ fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
+ if(fd >= 0) {
+ struct stat st = {};
+ errno = EINVAL;
+ if(fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) {
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ close(fd);
+ pthread_setcancelstate(cs, 0);
+ return errno;
+ }
+ } else {
+ if(errno != ENOENT && errno != ENOTDIR) {
+ return errno;
+ }
+ f = fopen("/etc/shadow", "rbe");
+ if(!f) {
+ if(errno != ENOENT && errno != ENOTDIR) {
+ return errno;
+ }
+ return 0;
+ }
+ }
+
+ pthread_cleanup_push(cleanup, f);
+ while(fgets(buf, size, f) && (k = strlen(buf)) > 0) {
+ if(skip || strncmp(name, buf, l) || buf[l] != ':') {
+ skip = buf[k - 1] != '\n';
+ continue;
+ }
+ if(buf[k - 1] != '\n') {
+ rv = ERANGE;
+ break;
+ }
+
+ if(__parsespent(buf, sp) < 0) {
+ continue;
+ }
+ *res = sp;
+ break;
+ }
+ pthread_cleanup_pop(1);
+ errno = rv ? rv : orig_errno;
+ return rv;
+}
+
+int lckpwdf(void) {
+ mlibc::infoLogger() << "mlibc: lckpwdf is unimplemented like musl" << frg::endlog;
+ return 0;
+}
+
+int ulckpwdf(void) {
+ mlibc::infoLogger() << "mlibc: ulckpwdf is unimplemented like musl" << frg::endlog;
+ return 0;
+}
+
+// Musl defines LINE_LIM to 256
+#define LINE_LIM 256
+
+struct spwd *getspnam(const char *name) {
+ static struct spwd sp;
+ static char *line;
+ struct spwd *res;
+ int e;
+ int orig_errno = errno;
+
+ if(!line) {
+ line = (char *)malloc(LINE_LIM);
+ }
+ if(!line) {
+ return 0;
+ }
+ e = getspnam_r(name, &sp, line, LINE_LIM, &res);
+ errno = e ? e : orig_errno;
+ return res;
+}
+
+struct spwd *fgetspent(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+void endspent(void) {
+ mlibc::infoLogger() << "mlibc: endspent is a stub" << frg::endlog;
+}
diff --git a/lib/mlibc/options/glibc/generic/stdio_ext-stubs.cpp b/lib/mlibc/options/glibc/generic/stdio_ext-stubs.cpp
new file mode 100644
index 0000000..b9b61fc
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/stdio_ext-stubs.cpp
@@ -0,0 +1,64 @@
+
+#include <stdio_ext.h>
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+
+size_t __fbufsize(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+size_t __fpending(FILE *file_base) {
+ __ensure(file_base->__dirty_end >= file_base->__dirty_begin);
+ return file_base->__dirty_end - file_base->__dirty_begin;
+}
+
+int __flbf(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+int __freadable(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+int __fwritable(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int __freading(FILE *file_base) {
+ return file_base->__io_mode == 0;
+}
+
+int __fwriting(FILE *file_base) {
+ return file_base->__io_mode == 1;
+}
+
+int __fsetlocking(FILE *, int) {
+ mlibc::infoLogger() << "mlibc: __fsetlocking() is a no-op" << frg::endlog;
+ return FSETLOCKING_INTERNAL;
+}
+
+void _flushlbf(void) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+// The following functions are defined by musl.
+
+size_t __freadahead(FILE *file_base) {
+ if(file_base->__io_mode != 0) {
+ mlibc::infoLogger() << "mlibc: __freadahead() called but file is not open for reading" << frg::endlog;
+ return 0;
+ }
+ return file_base->__valid_limit - file_base->__offset;
+}
+const char *__freadptr(FILE *, size_t *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+void __fseterr(FILE *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
diff --git a/lib/mlibc/options/glibc/generic/string.cpp b/lib/mlibc/options/glibc/generic/string.cpp
new file mode 100644
index 0000000..19f77c7
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/string.cpp
@@ -0,0 +1,32 @@
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <type_traits>
+#include <string.h>
+
+/* This is a bit of a weird detail of the GNU implementation and C's lack of
+ * overloading and strictness: GNU takes const char * and returns a char * so
+ * that it autocasts to your desired constness, this function never actually
+ * modifies the string.
+ */
+char *__mlibc_gnu_basename_c(const char *path) {
+ char *basename_component = strrchr(path, '/');
+ if (!basename_component) {
+ return const_cast<char *>(path);
+ }
+ return basename_component + 1;
+}
+
+
+/* GNU exposes these overloads, and as a result, we should probably have them
+ * checked, to make sure we actually match expectations.
+ */
+static_assert(
+ std::is_same_v<decltype(basename((const char *)nullptr)), const char*>,
+ "C++ overloads broken"
+);
+
+static_assert(
+ std::is_same_v<decltype(basename((char *)nullptr)), char*>,
+ "C++ overloads broken"
+);
diff --git a/lib/mlibc/options/glibc/generic/sys-io.cpp b/lib/mlibc/options/glibc/generic/sys-io.cpp
new file mode 100644
index 0000000..fbd9070
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/sys-io.cpp
@@ -0,0 +1,25 @@
+#include <errno.h>
+#include <sys/io.h>
+
+#include <bits/ensure.h>
+#include <mlibc/glibc-sysdeps.hpp>
+
+int ioperm(unsigned long int from, unsigned long int num, int turn_on) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ioperm, -1);
+
+ if(int e = sysdep(from, num, turn_on); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+int iopl(int level) {
+ auto sysdep = MLIBC_CHECK_OR_ENOSYS(mlibc::sys_iopl, -1);
+
+ if(int e = sysdep(level); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/mlibc/options/glibc/generic/sys-ioctl.cpp b/lib/mlibc/options/glibc/generic/sys-ioctl.cpp
new file mode 100644
index 0000000..021d2a3
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/sys-ioctl.cpp
@@ -0,0 +1,21 @@
+
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/debug.hpp>
+#include <mlibc/glibc-sysdeps.hpp>
+
+int ioctl(int fd, unsigned long request, ...) {
+ va_list args;
+ va_start(args, request);
+ int result;
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_ioctl, -1);
+ void *arg = va_arg(args, void *);
+ if(int e = mlibc::sys_ioctl(fd, request, arg, &result); e) {
+ errno = e;
+ return -1;
+ }
+ return result;
+}
+
diff --git a/lib/mlibc/options/glibc/generic/sys-timex.cpp b/lib/mlibc/options/glibc/generic/sys-timex.cpp
new file mode 100644
index 0000000..6173399
--- /dev/null
+++ b/lib/mlibc/options/glibc/generic/sys-timex.cpp
@@ -0,0 +1,17 @@
+#include <bits/ensure.h>
+#include <sys/timex.h>
+
+int adjtimex(struct timex *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int clock_adjtime(clockid_t, struct timex *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+int ntp_adjtime(struct timex *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}