aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix/generic/posix_stdio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/posix/generic/posix_stdio.cpp')
-rw-r--r--lib/mlibc/options/posix/generic/posix_stdio.cpp209
1 files changed, 209 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/posix_stdio.cpp b/lib/mlibc/options/posix/generic/posix_stdio.cpp
new file mode 100644
index 0000000..fc77a54
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/posix_stdio.cpp
@@ -0,0 +1,209 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <bits/ensure.h>
+#include <mlibc/ansi-sysdeps.hpp>
+#include <mlibc/debug.hpp>
+#include <mlibc/file-io.hpp>
+#include <mlibc/posix-file-io.hpp>
+#include <mlibc/posix-sysdeps.hpp>
+
+struct popen_file : mlibc::fd_file {
+ popen_file(int fd, void (*do_dispose)(abstract_file *) = nullptr)
+ : fd_file(fd, do_dispose) {}
+
+ pid_t get_popen_pid() {
+ return _popen_pid;
+ }
+
+ void set_popen_pid(pid_t new_pid) {
+ _popen_pid = new_pid;
+ }
+
+private:
+ // Underlying PID in case of popen()
+ pid_t _popen_pid;
+};
+
+FILE *fmemopen(void *buf, size_t size, const char *__restrict mode) {
+ int flags = mlibc::fd_file::parse_modestring(mode);
+
+ return frg::construct<mlibc::fmemopen_mem_file>(getAllocator(), buf, size, flags,
+ mlibc::file_dispose_cb<mlibc::mem_file>);
+}
+
+int pclose(FILE *stream) {
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_waitpid, -1);
+
+ auto file = static_cast<popen_file *>(stream);
+
+ int status;
+ pid_t pid = file->get_popen_pid();
+
+ fclose(file);
+
+ if (mlibc::sys_waitpid(pid, &status, 0, NULL, &pid) != 0) {
+ errno = ECHILD;
+ return -1;
+ }
+
+ return status;
+}
+
+FILE *popen(const char *command, const char *typestr) {
+ bool is_write;
+ pid_t child;
+ FILE *ret = nullptr;
+
+ MLIBC_CHECK_OR_ENOSYS(mlibc::sys_fork && mlibc::sys_dup2 && mlibc::sys_execve &&
+ mlibc::sys_sigprocmask && mlibc::sys_sigaction && mlibc::sys_pipe, nullptr);
+
+ if (typestr == NULL) {
+ errno = EINVAL;
+ return nullptr;
+ }
+
+ if (strstr(typestr, "w") != NULL) {
+ is_write = true;
+ } else if (strstr(typestr, "r") != NULL) {
+ is_write = false;
+ } else {
+ errno = EINVAL;
+ return nullptr;
+ }
+
+ bool cloexec = false;
+ if (strstr(typestr, "e") != NULL) {
+ // Set FD_CLOEXEC on the new file descriptor
+ cloexec = true;
+ }
+
+ int fds[2];
+ if (int e = mlibc::sys_pipe(fds, 0)) {
+ errno = e;
+ return nullptr;
+ }
+
+ struct sigaction new_sa, old_int, old_quit;
+ sigset_t new_mask, old_mask;
+
+ new_sa.sa_handler = SIG_IGN;
+ new_sa.sa_flags = 0;
+ sigemptyset(&new_sa.sa_mask);
+ mlibc::sys_sigaction(SIGINT, &new_sa, &old_int);
+ mlibc::sys_sigaction(SIGQUIT, &new_sa, &old_quit);
+
+ sigemptyset(&new_mask);
+ sigaddset(&new_mask, SIGCHLD);
+ mlibc::sys_sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
+
+ int parent_end = is_write ? 1 : 0;
+ int child_end = is_write ? 0 : 1;
+
+ if (int e = mlibc::sys_fork(&child)) {
+ errno = e;
+ mlibc::sys_close(fds[0]);
+ mlibc::sys_close(fds[1]);
+ } else if (!child) {
+ // For the child
+ mlibc::sys_sigaction(SIGINT, &old_int, nullptr);
+ mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr);
+ mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+
+ mlibc::sys_close(fds[parent_end]);
+
+ if (mlibc::sys_dup2(fds[child_end], 0, is_write ? 0 : 1)) {
+ __ensure(!"sys_dup2() failed in popen()");
+ }
+ mlibc::sys_close(fds[child_end]);
+
+ const char *args[] = {
+ "sh", "-c", command, nullptr
+ };
+
+ mlibc::sys_execve("/bin/sh", const_cast<char **>(args), environ);
+ _Exit(127);
+ } else {
+ // For the parent
+ mlibc::sys_close(fds[child_end]);
+
+ ret = frg::construct<popen_file>(
+ getAllocator(),
+ fds[parent_end],
+ mlibc::file_dispose_cb<popen_file>
+ );
+ __ensure(ret);
+
+ auto file = static_cast<popen_file *>(ret);
+
+ file->set_popen_pid(child);
+
+ if (cloexec == true) {
+ fcntl(file->fd(), F_SETFD, O_CLOEXEC);
+ }
+ }
+
+ mlibc::sys_sigaction(SIGINT, &old_int, nullptr);
+ mlibc::sys_sigaction(SIGQUIT, &old_quit, nullptr);
+ mlibc::sys_sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+
+ return ret;
+}
+
+FILE *open_memstream(char **buf, size_t *sizeloc) {
+ return frg::construct<mlibc::memstream_mem_file>(getAllocator(), buf, sizeloc, O_RDWR,
+ mlibc::file_dispose_cb<mlibc::mem_file>);
+}
+
+int fseeko(FILE *file_base, off_t offset, int whence) {
+ auto file = static_cast<mlibc::abstract_file *>(file_base);
+ if(int e = file->seek(offset, whence); e) {
+ errno = e;
+ return -1;
+ }
+ return 0;
+}
+
+off_t ftello(FILE *file_base) {
+ auto file = static_cast<mlibc::abstract_file *>(file_base);
+ off_t current_offset;
+ if(int e = file->tell(&current_offset); e) {
+ errno = e;
+ return -1;
+ }
+ return current_offset;
+}
+
+int dprintf(int fd, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = vdprintf(fd, format, args);
+ va_end(args);
+ return result;
+}
+
+int vdprintf(int fd, const char *format, __builtin_va_list args) {
+ mlibc::fd_file file{fd};
+ int ret = vfprintf(&file, format, args);
+ file.flush();
+ return ret;
+}
+
+char *fgetln(FILE *, size_t *) {
+ __ensure(!"Not implemented");
+ __builtin_unreachable();
+}
+
+FILE *fopencookie(void *cookie, const char *__restrict mode, cookie_io_functions_t funcs) {
+ int flags = mlibc::fd_file::parse_modestring(mode);
+
+ return frg::construct<mlibc::cookie_file>(getAllocator(), cookie, flags, funcs,
+ mlibc::file_dispose_cb<mlibc::cookie_file>);
+}