From a52ceb05e49d6d5b5adeaa733037d558187848f7 Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Sun, 7 Aug 2022 17:45:11 +0200
Subject: user/shell: make `>>` work for executables

---
 src/user/app/shell/shell.c | 41 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 38 insertions(+), 3 deletions(-)

(limited to 'src/user')

diff --git a/src/user/app/shell/shell.c b/src/user/app/shell/shell.c
index 66725a1..505997a 100644
--- a/src/user/app/shell/shell.c
+++ b/src/user/app/shell/shell.c
@@ -62,9 +62,44 @@ static void run_args(int argc, char **argv, struct redir *redir) {
 		return;
 	}
 
-	if (redir->stdout && !freopen(redir->stdout, redir->append ? "a" : "w", stdout)) {
-		eprintf("couldn't open %s for redirection", redir->stdout);
-		exit(0);
+	if (redir->stdout) {
+		FILE *f = fopen(redir->stdout, redir->append ? "a" : "w");
+		if (!f) {
+			eprintf("couldn't open %s for redirection", redir->stdout);
+			exit(1);
+		}
+
+		/* a workaround for file offsets not being preserved across exec()s.
+		 * TODO document that weird behaviour of exec() */
+
+		handle_t p[2];
+		if (_syscall_pipe(p, 0) < 0) {
+			eprintf("couldn't create redirection pipe", redir->stdout);
+			exit(1);
+		}
+		if (!_syscall_fork(FORK_NOREAP, NULL)) {
+			/* the child forwards data from the pipe to the file */
+			const size_t buflen = 512;
+			char *buf = malloc(buflen);
+			if (!buf) {
+				eprintf("when redirecting: malloc failure");
+				exit(1);
+			}
+			close(p[1]);
+			for (;;) {
+				long len = _syscall_read(p[0], buf, buflen, 0);
+				if (len < 0) exit(0);
+				fwrite(buf, 1, len, f);
+				if (ferror(f)) exit(0);
+			}
+		}
+
+		fclose(f);
+		close(p[0]);
+		if (_syscall_dup(p[1], 1, 0) < 0) {
+			eprintf("dup failure");
+			exit(1);
+		}
 	}
 
 	for (struct builtin *iter = builtins; iter->name; iter++) {
-- 
cgit v1.2.3