From e85cb6bc1028fc9aa53ce8ecdaa45b11f416fa89 Mon Sep 17 00:00:00 2001
From: BanceDev <lanceaborden@protonmail.com>
Date: Mon, 23 Sep 2024 12:08:03 -0400
Subject: [PATCH] implemented background process operator

---
 src/builtins.c | 144 +++++++++++++++++++++++++++++++++
 src/history.c  |   2 +-
 src/history.h  |  24 ------
 src/lua_api.c  |   1 -
 src/lush.c     | 210 +++++++++++++++++++------------------------------
 src/lush.h     |  11 +++
 6 files changed, 238 insertions(+), 154 deletions(-)
 create mode 100644 src/builtins.c
 delete mode 100644 src/history.h

diff --git a/src/builtins.c b/src/builtins.c
new file mode 100644
index 0000000..5b032c1
--- /dev/null
+++ b/src/builtins.c
@@ -0,0 +1,144 @@
+/*
+Copyright (c) 2024, Lance Borden
+All rights reserved.
+
+This software is licensed under the BSD 3-Clause License.
+You may obtain a copy of the license at:
+https://opensource.org/licenses/BSD-3-Clause
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted under the conditions stated in the BSD 3-Clause
+License.
+
+THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#include "help.h"
+#include "lua.h"
+#include "lua_api.h"
+#include "lush.h"
+#include <asm-generic/ioctls.h>
+#include <bits/time.h>
+#include <dirent.h>
+#include <linux/limits.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+char *builtin_strs[] = {"cd", "help", "exit", "time"};
+char *builtin_usage[] = {"[dirname]", "", "", "[pipeline]"};
+
+int (*builtin_func[])(lua_State *, char ***) = {
+	&lush_cd, &lush_help, &lush_exit, &lush_time, &lush_lua};
+
+int lush_num_builtins() { return sizeof(builtin_strs) / sizeof(char *); }
+
+int lush_cd(lua_State *L, char ***args) {
+	uid_t uid = getuid();
+	struct passwd *pw = getpwuid(uid);
+	if (!pw) {
+		perror("retrieve home dir");
+		return 0;
+	}
+	if (args[0][1] == NULL) {
+		if (chdir(pw->pw_dir) != 0) {
+			perror("lush: cd");
+		}
+	} else {
+		char path[PATH_MAX];
+		char extended_path[PATH_MAX];
+		char *tilda = strchr(args[0][1], '~');
+		// first check if they want to go to old dir
+		if (strcmp("-", args[0][1]) == 0) {
+			strcpy(path, getenv("OLDPWD"));
+		} else if (tilda) {
+			strcpy(path, pw->pw_dir);
+			strcat(path, tilda + 1);
+		} else {
+			strcpy(path, args[0][1]);
+		}
+
+		char *exp_path = realpath(path, extended_path);
+		if (!exp_path) {
+			perror("realpath");
+			return 1;
+		}
+
+		char *cwd = getcwd(NULL, 0);
+		setenv("OLDPWD", cwd, 1);
+		free(cwd);
+
+		if (chdir(exp_path) != 0) {
+			perror("lush: cd");
+		}
+	}
+
+	return 1;
+}
+
+int lush_help(lua_State *L, char ***args) {
+	printf("%s\n", lush_get_help_text());
+#ifdef LUSH_VERSION
+	printf("Lunar Shell, version %s\n", LUSH_VERSION);
+#endif
+	printf("These shell commands are defined internally. Type 'help' at any "
+		   "time to reference this list.\n");
+	printf("Available commands: \n");
+	for (int i = 0; i < lush_num_builtins(); i++) {
+		printf("- %s %s\n", builtin_strs[i], builtin_usage[i]);
+	}
+	return 1;
+}
+
+int lush_exit(lua_State *L, char ***args) { exit(0); }
+
+int lush_time(lua_State *L, char ***args) {
+	// advance past time command
+	args[0]++;
+
+	// count commands
+	int i = 0;
+	while (args[i++]) {
+		;
+	}
+
+	// get time
+	struct timespec start, end;
+	double elapsed_time;
+
+	clock_gettime(CLOCK_MONOTONIC, &start);
+	int rc = lush_run(L, args, i);
+	clock_gettime(CLOCK_MONOTONIC, &end);
+
+	elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0 +
+				   (end.tv_nsec - start.tv_nsec) / 1e6;
+	printf("Time: %.3f milliseconds\n", elapsed_time);
+
+	// return pointer back to "time" for free()
+	args[0]--;
+	return rc;
+}
+
+int lush_lua(lua_State *L, char ***args) {
+	// run the lua file given
+	const char *script = args[0][0];
+	// move args forward to any command line args
+	args[0]++;
+
+	lua_load_script(L, script, args[0]);
+
+	// return pointer back to lua file
+	args[0]--;
+	return 1;
+}
diff --git a/src/history.c b/src/history.c
index 511f12a..93d6983 100644
--- a/src/history.c
+++ b/src/history.c
@@ -15,7 +15,7 @@ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
 
-#include "history.h"
+#include "lush.h"
 #include <linux/limits.h>
 #include <pwd.h>
 #include <stdbool.h>
diff --git a/src/history.h b/src/history.h
deleted file mode 100644
index 9507126..0000000
--- a/src/history.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-Copyright (c) 2024, Lance Borden
-All rights reserved.
-
-This software is licensed under the BSD 3-Clause License.
-You may obtain a copy of the license at:
-https://opensource.org/licenses/BSD-3-Clause
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted under the conditions stated in the BSD 3-Clause
-License.
-
-THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-*/
-
-#ifndef HISTORY_H
-#define HISTORY_H
-
-char *lush_get_past_command(int pos);
-void lush_push_history(const char* line);
-
-#endif
diff --git a/src/lua_api.c b/src/lua_api.c
index d40489c..05742cc 100644
--- a/src/lua_api.c
+++ b/src/lua_api.c
@@ -16,7 +16,6 @@ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
 
 #include "lua_api.h"
-#include "history.h"
 #include "lush.h"
 #include <dirent.h>
 #include <lauxlib.h>
diff --git a/src/lush.c b/src/lush.c
index 1cdb50d..3318807 100644
--- a/src/lush.c
+++ b/src/lush.c
@@ -17,8 +17,6 @@ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
 #include "lush.h"
 #include "hashmap.h"
-#include "help.h"
-#include "history.h"
 #include "lauxlib.h"
 #include "lua.h"
 #include "lua_api.h"
@@ -73,114 +71,6 @@ void lush_add_alias(const char *alias, const char *command) {
 
 char *lush_get_alias(char *alias) { return hm_get(aliases, alias); }
 
-// -- builtin functions --
-char *builtin_strs[] = {"cd", "help", "exit", "time"};
-char *builtin_usage[] = {"[dirname]", "", "", "[pipeline]"};
-
-int (*builtin_func[])(lua_State *, char ***) = {
-	&lush_cd, &lush_help, &lush_exit, &lush_time, &lush_lua};
-
-int lush_num_builtins() { return sizeof(builtin_strs) / sizeof(char *); }
-
-int lush_cd(lua_State *L, char ***args) {
-	uid_t uid = getuid();
-	struct passwd *pw = getpwuid(uid);
-	if (!pw) {
-		perror("retrieve home dir");
-		return 0;
-	}
-	if (args[0][1] == NULL) {
-		if (chdir(pw->pw_dir) != 0) {
-			perror("lush: cd");
-		}
-	} else {
-		char path[PATH_MAX];
-		char extended_path[PATH_MAX];
-		char *tilda = strchr(args[0][1], '~');
-		// first check if they want to go to old dir
-		if (strcmp("-", args[0][1]) == 0) {
-			strcpy(path, getenv("OLDPWD"));
-		} else if (tilda) {
-			strcpy(path, pw->pw_dir);
-			strcat(path, tilda + 1);
-		} else {
-			strcpy(path, args[0][1]);
-		}
-
-		char *exp_path = realpath(path, extended_path);
-		if (!exp_path) {
-			perror("realpath");
-			return 1;
-		}
-
-		char *cwd = getcwd(NULL, 0);
-		setenv("OLDPWD", cwd, 1);
-		free(cwd);
-
-		if (chdir(exp_path) != 0) {
-			perror("lush: cd");
-		}
-	}
-
-	return 1;
-}
-
-int lush_help(lua_State *L, char ***args) {
-	printf("%s\n", lush_get_help_text());
-#ifdef LUSH_VERSION
-	printf("Lunar Shell, version %s\n", LUSH_VERSION);
-#endif
-	printf("These shell commands are defined internally. Type 'help' at any "
-		   "time to reference this list.\n");
-	printf("Available commands: \n");
-	for (int i = 0; i < lush_num_builtins(); i++) {
-		printf("- %s %s\n", builtin_strs[i], builtin_usage[i]);
-	}
-	return 1;
-}
-
-int lush_exit(lua_State *L, char ***args) { exit(0); }
-
-int lush_time(lua_State *L, char ***args) {
-	// advance past time command
-	args[0]++;
-
-	// count commands
-	int i = 0;
-	while (args[i++]) {
-		;
-	}
-
-	// get time
-	struct timespec start, end;
-	double elapsed_time;
-
-	clock_gettime(CLOCK_MONOTONIC, &start);
-	int rc = lush_run(L, args, i);
-	clock_gettime(CLOCK_MONOTONIC, &end);
-
-	elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0 +
-				   (end.tv_nsec - start.tv_nsec) / 1e6;
-	printf("Time: %.3f milliseconds\n", elapsed_time);
-
-	// return pointer back to "time" for free()
-	args[0]--;
-	return rc;
-}
-
-int lush_lua(lua_State *L, char ***args) {
-	// run the lua file given
-	const char *script = args[0][0];
-	// move args forward to any command line args
-	args[0]++;
-
-	lua_load_script(L, script, args[0]);
-
-	// return pointer back to lua file
-	args[0]--;
-	return 1;
-}
-
 // -- shell utility --
 
 static void set_raw_mode(struct termios *orig_termios) {
@@ -949,8 +839,7 @@ char *lush_resolve_aliases(char *line) {
 }
 
 static int is_operator(const char *str) {
-	const char *operators[] = {"||", "&&", "&", ";", ">>", ">", "<",
-							   "\\", "(",  ")", "{", "}",  "!", "|"};
+	const char *operators[] = {"||", "&&", ">>", ">", "<", "&", ";", "|"};
 	int num_operators = sizeof(operators) / sizeof(operators[0]);
 	for (int i = 0; i < num_operators; i++) {
 		if (strncmp(str, operators[i], strlen(operators[i])) == 0) {
@@ -960,25 +849,55 @@ static int is_operator(const char *str) {
 			case 1:
 				return OP_AND;
 			case 2:
-				return OP_BACKGROUND;
+				return OP_APPEND_OUT;
 			case 3:
-				return OP_SEMICOLON;
+				return OP_REDIRECT_OUT;
 			case 4:
-				return OP_APPEND_OUT;
+				return OP_REDIRECT_IN;
 			case 5:
-				return OP_REDIRECT_OUT;
+				return OP_BACKGROUND;
 			case 6:
-				return OP_REDIRECT_IN;
-			case 13:
+				return OP_SEMICOLON;
+			case 7:
 				return OP_PIPE;
 			default:
-				return OP_OTHER; // Parentheses, braces, etc.
+				return 0;
 			}
 		}
 	}
 	return 0; // Not an operator
 }
 
+static int operator_length(const char *str) {
+	const char *operators[] = {"||", "&&", ">>", ">", "<", "&", ";", "|"};
+	int num_operators = sizeof(operators) / sizeof(operators[0]);
+	for (int i = 0; i < num_operators; i++) {
+		if (strncmp(str, operators[i], strlen(operators[i])) == 0) {
+			switch (i) {
+			case 0:
+				return 2;
+			case 1:
+				return 2;
+			case 2:
+				return 2;
+			case 3:
+				return 1;
+			case 4:
+				return 1;
+			case 5:
+				return 1;
+			case 6:
+				return 1;
+			case 7:
+				return 1;
+			default:
+				return 0;
+			}
+		}
+	}
+	return 0;
+}
+
 static char *trim_whitespace(char *str) {
 	char *end;
 
@@ -1015,7 +934,7 @@ char **lush_split_commands(char *line) {
 			start++;
 
 		// Check for operators
-		int op_len = is_operator(start);
+		int op_len = operator_length(start);
 		if (op_len > 0) {
 			// Allocate memory for operator command
 			char *operator_cmd = calloc(op_len + 1, sizeof(char));
@@ -1147,6 +1066,22 @@ static int run_command(lua_State *L, char ***commands) {
 	return lush_execute_command(commands[0], STDIN_FILENO, STDOUT_FILENO);
 }
 
+static int run_command_background(lua_State *L, char ***commands) {
+	pid_t pid = fork();
+
+	if (pid < 0) {
+		perror("fork");
+		return -1;
+	} else if (pid == 0) {
+		execvp(commands[0][0], commands[0]);
+		perror("execvp background");
+		exit(EXIT_FAILURE);
+	} else {
+		printf("Process %d running in background\n", pid);
+		return 0;
+	}
+}
+
 int lush_execute_chain(lua_State *L, char ***commands, int num_commands) {
 	if (commands[0][0][0] == '\0') {
 		return 1;
@@ -1190,6 +1125,13 @@ int lush_execute_chain(lua_State *L, char ***commands, int num_commands) {
 				commands += 2;
 				continue;
 			}
+			// Handle '&' operator for background execution
+			if (op_type == OP_BACKGROUND) {
+
+				run_command_background(L, commands);
+				commands += 2;
+				continue;
+			}
 		}
 
 		if (!is_operator(commands[0][0])) {
@@ -1198,7 +1140,7 @@ int lush_execute_chain(lua_State *L, char ***commands, int num_commands) {
 		}
 	}
 
-	return last_result;
+	return 1;
 }
 
 int lush_execute_pipeline(char ***commands, int num_commands) {
@@ -1273,6 +1215,7 @@ int lush_execute_command(char **args, int input_fd, int output_fd) {
 		// execute the command
 		if (execvp(args[0], args) == -1) {
 			perror("execvp");
+			printf("%s\n", args[0]);
 			exit(EXIT_FAILURE);
 		}
 	} else if (pid < 0) {
@@ -1288,9 +1231,8 @@ int lush_execute_command(char **args, int input_fd, int output_fd) {
 
 	if (WIFEXITED(status)) {
 		return WEXITSTATUS(status);
-	} else {
-		return -1;
 	}
+	return -1;
 }
 
 int lush_run(lua_State *L, char ***commands, int num_commands) {
@@ -1302,6 +1244,12 @@ int lush_run(lua_State *L, char ***commands, int num_commands) {
 	return lush_execute_chain(L, commands, num_commands);
 }
 
+static void background_handler(int sig) {
+	// Reap all child processes
+	while (waitpid(-1, NULL, WNOHANG) > 0)
+		;
+}
+
 int main(int argc, char *argv[]) {
 	// check if the --version arg was passed
 	if (argc > 1 && strcmp(argv[1], "--version") == 0) {
@@ -1343,11 +1291,17 @@ int main(int argc, char *argv[]) {
 	}
 
 	// eat ^C in main
-	struct sigaction sa;
-	sa.sa_handler = SIG_IGN;
-	sa.sa_flags = 0;
-	sigemptyset(&sa.sa_mask);
-	sigaction(SIGINT, &sa, NULL);
+	struct sigaction sa_int;
+	sa_int.sa_handler = SIG_IGN;
+	sa_int.sa_flags = 0;
+	sigemptyset(&sa_int.sa_mask);
+	sigaction(SIGINT, &sa_int, NULL);
+
+	struct sigaction sa_bk;
+	sa_bk.sa_handler = &background_handler;
+	sigemptyset(&sa_bk.sa_mask);
+	sa_bk.sa_flags = SA_RESTART;
+	sigaction(SIGCHLD, &sa_bk, NULL);
 
 	// set custom envars
 	char hostname[256];
diff --git a/src/lush.h b/src/lush.h
index 291a8dd..d6022b4 100644
--- a/src/lush.h
+++ b/src/lush.h
@@ -20,10 +20,17 @@ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
 #include <lua.h>
 
+// alias
 void lush_add_alias(const char *alias, const char *command);
 char *lush_get_alias(char *alias);
 char *lush_resolve_aliases(char *line);
 
+// builtins
+extern char *builtin_strs[];
+extern char *builtin_usage[];
+
+extern int (*builtin_func[])(lua_State *, char ***);
+
 int lush_cd(lua_State *L, char ***args);
 int lush_help(lua_State *L, char ***args);
 int lush_exit(lua_State *L, char ***args);
@@ -47,4 +54,8 @@ void lush_format_prompt(const char *prompt_format);
 // format spec for the prompt
 extern char *prompt_format;
 
+// history
+char *lush_get_past_command(int pos);
+void lush_push_history(const char* line);
+
 #endif // LUSH_H