tag v0.10.29b
Tagger: Alexeev Bronislav <bro.alexeev@inbox.ru>
Date:   Sun Sep 8 22:39:05 2024 +0700

version 0.10.29beta

commit 1f401f1f9b0a23ea1dba44c86a9e1f8cc4361ce6
Author: Alexeev Bronislav <bro.alexeev@inbox.ru>
Date:   Sun Sep 8 22:36:46 2024 +0700

    feat/docs/fix: add delims support, fix bugs, improve user experience and improve docs

diff --git a/README.md b/README.md
index 7934692..b4c52d3 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,9 @@ Blazing fast, super simple and indestructible program interpretator (shell) in C
 > [!NOTE]
 > At the moment, shegang is under active development (alpha), many things may not work, and this version is not recommended for use (all at your own risk).
 
- > Current programm state: Alpha
+ > Current program state: Beta (0.10.29b)
+
+*Stable Alpha Release*: [0.9.25alpha](https://github.com/alxvdev/shegang/releases/tag/0.9.25a)
 
 You can see [commits changelog here](./CHANGELOG.md) or see [history of changes here](./docs/en/changes.md).
 
@@ -43,6 +45,7 @@ shegang comes packed with a wide range of features to enhance your command-line
  - __Colorschemes__: shegang have custom color schemes for syntax highlight (you can see in section "usage")
  - __Plugin system__: shegang have plugin manager, plugin loader and example of plugins for shell. Plugins writen in C and compiled to .so files (and load to shegang)
  - __Environment variables__: shegang can set or get value of environment variable with `environment` util.
+ - __Delimiters__: shegang can split line by delims (&&, &, ||, |, ;)
 
 ## Planned Features
 Me and shegang community is constantly working to improve and expand the shell's capabilities. Some of the planned features include:
@@ -53,7 +56,7 @@ Me and shegang community is constantly working to improve and expand the shell's
 4. *Comprehensive customization*
 5. *Productivity-enhancing tools*
 6. *Integrated minimalistic text editor like nano or vi*
-7. *Pipes (>, >>) and delims (&&, ||)*
+7. *Pipes (>, >>) for redirect command output*
 
 ### Known uncorrected bugs
 Unfortunately, shegang has bugs that can interfere with the user experience. Below we have placed a list of known bugs that have not been fixed. You can submit bug solution.
diff --git a/build_scripts/create_package.sh b/build_scripts/create_package.sh
index c5be7c1..8396f1c 100755
--- a/build_scripts/create_package.sh
+++ b/build_scripts/create_package.sh
@@ -42,6 +42,10 @@ echo -e "${CYAN}Copy README...${PLAIN}"
 cp ../README.md -r $DIR_NAME
 echo -e "${CYAN}Copy LICENSE...${PLAIN}"
 cp ../LICENSE -r $DIR_NAME
+echo -e "${CYAN}Copy libisocline.a...${PLAIN}"
+cp ../libisocline.a -r $DIR_NAME
+echo -e "${CYAN}Copy pipegang plugin...${PLAIN}"
+cp ../pipegang_plugin.so -r $DIR_NAME
 
 echo -e "${CYAN}Package files: ${PLAIN}"
 tree $DIR_NAME
diff --git a/docs/en/changes.md b/docs/en/changes.md
index a9b27bd..87ab575 100644
--- a/docs/en/changes.md
+++ b/docs/en/changes.md
@@ -38,9 +38,14 @@ Versions with main updates.
 0.7.23a - add custom colorschemes for syntax highlight, fix bugs
 0.8.23a - create plugin manager system, basic plugins loader and example plugin
 0.8.24a - refactor get_color_by_name function
+0.8.25a - fix bugs
+0.9.25a - create environment variables system
+0.9.26a - improve command history system
 
 ## Beta (b)
-No updates.
+0.10.27b - improve docs, command executor structure
+0.10.28b - improve tokenizer
+0.10.29b - fix bugs, improve user experience
 
 ## Pre-release (pr)
 
diff --git a/docs/ru/article-part2.md b/docs/ru/article-part2.md
index 4645016..e57abaf 100644
--- a/docs/ru/article-part2.md
+++ b/docs/ru/article-part2.md
@@ -1 +1,10 @@
 # Программирование на C в Linux на примере создания шелла: часть 2
+Я решил недавно улучшить свой навык владения C, путем написания проектов. Самая первая мысль, которая пришла мне на ум - это командный интерпретатор, командная оболочка, shell проще говоря. А также я расскажу о системе сборки make, и о том, как правильно писать и документировать C-код.
+
+В [первой части](https://habr.com/ru/companies/timeweb/articles/839890/) мы задали базовую структуру кода, разобрались с чтением вывода и созданием процессов. А в этой части нашей задачи будет дойти с альфа-стадии на бета-стадию - то есть реализовать прочий важный функционал, такой как: минимальная поддержка плагинов; автодополнение; подсветка синтаксиса; переменные окружения, новые встроенные утилиты.
+
+Да-да, мы превратим наш велосипед в мопед! Я вынес из прошлой статьи итоги, и попытался решить все проблемы и замечания. Продолжаем погружение в пучины разработки под Linux!
+
+---
+
+Я прочитал ваши комментарии под прошлой статьей, за замечания и советы я выражаю благодарность следующим пользователям: BulldozerBSG (за советы и книгу "Linux Application Development 2ed, K.Johnson", а также за разьяснение стандарта), bigbamblbee (за разьянение на тему GCC vs CLANG), grdvo (за совет), chekpoint (за улучшение кода и советы).
diff --git a/docs/ru/index.md b/docs/ru/index.md
index 2bf7054..41c29e2 100644
--- a/docs/ru/index.md
+++ b/docs/ru/index.md
@@ -8,3 +8,4 @@
 ## Дополнительный материал
 
  + [Программирование на C в Linux на примере создания своей командной оболочки / Алексеев Бронислав](./article.md)
+ + [Создаем свой командную оболочку на C, часть 2 / Алексеев Бронислав](./article-part2.md)
diff --git a/example_plugin.so b/example_plugin.so
deleted file mode 100755
index bf1614d..0000000
Binary files a/example_plugin.so and /dev/null differ
diff --git a/include/colors.h b/include/colors.h
index 7179d7f..bc527b0 100644
--- a/include/colors.h
+++ b/include/colors.h
@@ -33,6 +33,6 @@ void println_colored(const char* message, char* message_color);
 void print_colored(const char* message, char* message_color);
 void println(const char* message);
 void print_message(const char* message, int message_type);
-void display_ps(void);
+void display_ps(int status);
 
 #endif // COLORS_H
diff --git a/include/userinput.h b/include/userinput.h
index 634d23d..7019cd2 100644
--- a/include/userinput.h
+++ b/include/userinput.h
@@ -8,6 +8,8 @@ char* read_user_input(void);
 char* fgets_input(void);
 char** split_into_tokens(char* line);
 void setup_isocline(void);
+char* get_history_file_path(void);
 char* r_line(char* filename, int line_n);
+char** split_by_delims(char* line, const char* delim, int *num_tokens);
 
 #endif // USERINPUT_H
diff --git a/src/core/userinput.c b/src/core/userinput.c
index ebe29a7..d13c29b 100644
--- a/src/core/userinput.c
+++ b/src/core/userinput.c
@@ -37,6 +37,24 @@ char* r_line(char* filename, int line_n) {
 	return data;
 }
 
+char* get_history_file_path(void) {
+	char* home_dir = getenv("HOME");
+
+	if (home_dir == NULL) {
+		return NULL;
+	}
+
+	char* history_file_path = (char*)malloc(1024);
+
+	if (history_file_path == NULL) {
+		return NULL;
+	}
+
+	snprintf(history_file_path, 1024, "%s/.sghistory", home_dir);
+
+	return history_file_path;
+}
+
 /**
  * @brief      Setup isocline settings
  */
@@ -44,7 +62,8 @@ void setup_isocline(void) {
 	ic_style_def("kbd", "white underline");
 	ic_style_def("ic-prompt", "gray");
 
-	ic_set_history("history.txt", -1);
+	printf("%s\n", get_history_file_path());
+	ic_set_history(get_history_file_path(), 1024);
 
 	ic_set_default_completer(&completer, NULL);
 	ic_set_default_highlighter(highlighter, NULL);
@@ -80,6 +99,37 @@ char* read_user_input(void) {
 	return input_buffer;
 }
 
+/**
+ * @brief      Splits by delims.
+ *
+ * @param      line   The line
+ * @param      delims  The delimiters
+ *
+ * @return     splitted line
+ */
+char** split_by_delims(char* line, const char* delims, int *num_tokens) {
+	char **tokens = NULL;
+	char *token;
+	int i = 0;
+
+	char *line_copy = malloc(strlen(line) + 1);
+	strcpy(line_copy, line);
+
+	token = strtok(line_copy, delims);
+
+	while (token != NULL) {
+		tokens = realloc(tokens, (i + 1) * sizeof(char *));
+		tokens[i] = malloc(strlen(token) + 1);
+		strcpy(tokens[i], token);
+		i++;
+		token = strtok(NULL, delims);
+	}
+
+	*num_tokens = i;
+	free(line_copy);
+	return tokens;
+}
+
 /**
  * @brief      function for split line into tokens
  *
diff --git a/src/execution/builtin/base.c b/src/execution/builtin/base.c
index d43267c..e9d655d 100644
--- a/src/execution/builtin/base.c
+++ b/src/execution/builtin/base.c
@@ -8,7 +8,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <stdio.h>
-#include <readline/history.h>
 #include "colors.h"
 #include "tasks_processing.h"
 #include "config.h"
@@ -17,8 +16,7 @@ extern tasks tasks_structure;
 extern char* username_color;
 extern char* pwd_color;
 extern char* curr_time_color;
-
-extern int global_exit_code;
+extern int global_status_code;
 
 /**
  * @brief Function for change working directory
@@ -30,8 +28,8 @@ extern int global_exit_code;
 int change_directory(char** args) {
 	if (args[1] == NULL) {
 		print_message("Expected argument for \"cd\" command", ERROR);
-		global_exit_code = -1;
 	} else if (chdir(args[1]) != 0) {
+		global_status_code = -1;
 		print_message("Couldn't change directory", ERROR);
 	}
 
diff --git a/src/execution/executor.c b/src/execution/executor.c
index 9d4d784..d3983d0 100644
--- a/src/execution/executor.c
+++ b/src/execution/executor.c
@@ -16,6 +16,7 @@
 #include "builtin.h"
 #include "executor.h"
 #include "environment.h"
+#include "isocline.h"
 
 extern tasks tasks_structure;
 extern char* username_color;
@@ -25,7 +26,7 @@ extern char* curr_time_color;
 extern Alias aliases[MAX_ALIASES];
 extern int num_aliases;
 
-extern int global_exit_code;
+extern int global_status_code;
 
 /**
  * @brief      Function for print info about shegang
@@ -114,7 +115,28 @@ int quit_from_shell(char** args) {
  * @return     int Status code
  */
 int print_history(char** args) {
-	print_message("Sorry, command history managament coming soon", DEBUG);
+	if (args[1] != NULL && strcmp(args[1], "clear") == 0) {
+		printf("Clean...\n");
+		ic_history_clear();
+		return 1;
+	}
+
+	FILE* file = fopen(get_history_file_path(), "r");
+	if (file == NULL) {
+		perror("fopen");
+		return 1;
+	}
+
+	char line[256];
+	int lineNumber = 1;
+
+	while (fgets(line, sizeof(line), file)) {
+		printf("%3d: %s", lineNumber, line);
+		lineNumber++;
+	}
+
+	fclose(file);
+
 	return 1;
 }
 
@@ -151,6 +173,26 @@ int execute_alias(char **args) {
 	return 0;
 }
 
+int environment(char** args) {
+	if (args[1] == NULL || args[2] == NULL) {
+		global_status_code = -1;
+		println("Usage: environment <get> <var> OR environment <set> <var> <value>");
+		return 1;
+	}
+
+	if (strcmp(args[1], "get") == 0) {
+		printf("%s\n", get_environment_variable(args[2]));
+		return 1;
+	} else if (strcmp(args[1], "set") == 0 && args[2] != NULL && args[3] != NULL) {
+		char* result = set_environment_variable(args[2], args[3]);
+		printf("ENV: %s\n", result);
+		return 1;
+	}
+
+	println("Usage: environment <get> <var> OR environment <set> <var> <value>");
+	return 1;
+}
+
 /**
  * @brief      Function for execute command
  *
@@ -159,61 +201,47 @@ int execute_alias(char **args) {
  * @return     int Status code
  */
 int execute(char** args) {
-	if (args[0] == NULL)
-		return 1;
-
-	if (strcmp(args[0], "cd") == 0) {
-		return change_directory(args);
-	} else if (strcmp(args[0], "help") == 0) {
-		return help(args);
-	} else if (strcmp(args[0], "quit") == 0 || strcmp(args[0], "exit") == 0) {
-		return quit_from_shell(args); 
-	} else if (strcmp(args[0], "bg") == 0) {
-		return bg_tasks(args);
-	} else if (strcmp(args[0], "term") == 0) {
-		return term_background_task(args);
-	} else if (strcmp(args[0], "history") == 0) {
-		return print_history(args);
-	} else if (strcmp(args[0], "reload") == 0) {
-		return reload_shell(args);
-	} else if (strcmp(args[0], "shegang_config") == 0) {
-		return shegang_config(args);
-	} else if (strcmp(args[0], "sgls") == 0) {
-		return sgls(args);
-	} else if (strcmp(args[0], "sglsblk") == 0) {
-		return sglsblk(args);
-	} else if (strcmp(args[0], "gapf") == 0) {
-		return gapf(args);
-	} else if (strcmp(args[0], "alias") == 0) {
-		println("Aliases are read from the config file ~/.sgaliases. Alias template: `NAME=COMMAND`");
-		global_exit_code = -1;
-		return 1;
-	} else if (strcmp(args[0], "environment") == 0) {
-		if (args[1] == NULL || args[2] == NULL) {
-			global_exit_code = -1;
-			println("Usage: environment <get> <var> OR environment <set> <var> <value>");
-			return 1;
+	struct { char *text, *hint; int arguments_count; int (*command)(char**); } commands[] = {
+		//  Command            Minimal arguments count        Hint for command                       Pointer to command
+		{.text="cd"				, .arguments_count=1, .hint="Change directory: cd <dir>", 		.command=&change_directory},
+		{.text="exit"			, .arguments_count=0, .hint="Quit/exit from shell: exit", 		.command=&quit_from_shell},
+		{.text="help"			, .arguments_count=0, .hint="Help command. Support args.", 		.command=&help},
+		{.text="bg"				, .arguments_count=0, .hint="Print background tasks", 			.command=&bg_tasks},
+		{.text="term"			, .arguments_count=1, .hint="Terminate background task", 		.command=&term_background_task},
+		{.text="history"		, .arguments_count=0, .hint="Shows or clean command history", 	.command=&print_history},
+		{.text="reload"			, .arguments_count=0, .hint="Reload shell configuration", 		.command=&reload_shell},
+		{.text="shegang_config"	, .arguments_count=0, .hint="Configurate current session", 		.command=&shegang_config},
+		{.text="sgls"			, .arguments_count=0, .hint="list: sgls <dir> -p|t|l|a", 		.command=&sgls},
+		{.text="sglsblk"		, .arguments_count=0, .hint="lsblk command analogue", 			.command=&sglsblk},
+		{.text="gapf"			, .arguments_count=1, .hint="cat analogue: gapf <file>", 		.command=&gapf},
+		{.text="environment"	, .arguments_count=2, .hint="Env vars (view help environment)", .command=&environment},
+	};
+
+	const int len = sizeof(commands) / sizeof(commands[0]);
+	int status = 1;
+
+	for (int i = 0; i < len; ++i) {
+		if (strcmp(args[0], commands[i].text) == 0) {
+			if (strcmp(commands[i].text, "exit") == 0) {
+				commands[i].command(args);
+			}
+			if (commands[i].arguments_count > 0) {
+				for (int j = 1; j < commands[i].arguments_count + 1; ++j) {
+					if (args[j] == NULL) {
+						printf("[Argument Count Error at %s:%d] %s\n", commands[i].text, j, commands[i].hint);
+						global_status_code = -1;
+						return 1;
+					}
+				}
+			}
+
+			return commands[i].command(args);
 		}
-
-		if (strcmp(args[1], "get") == 0) {
-			printf("%s\n", get_environment_variable(args[2]));
-			return 1;
-		} else if (strcmp(args[1], "set") == 0 && args[2] != NULL && args[3] != NULL) {
-			char* result = set_environment_variable(args[2], args[3]);
-			printf("ENV: %s\n", result);
-			return 1;
-		}
-
-		println("Usage: environment <get> <var> OR environment <set> <var> <value>");
-		return 1;
-	} else {
-		if (execute_alias(args) == 0)
-			return launch_task(args);
 	}
 
-	return 1;
-
-	free(args);
+	if (execute_alias(args) == 0) {
+		return launch_task(args);
+	}
 
 	return 1;
 }
diff --git a/src/execution/tasks_processing.c b/src/execution/tasks_processing.c
index d6985fb..3c83fa7 100644
--- a/src/execution/tasks_processing.c
+++ b/src/execution/tasks_processing.c
@@ -40,10 +40,10 @@ const char *apps[] = {
 	"neofetch", "firefox", "alacritty", "mate-terminal",
 	"xfce4-terminal", "thunar", "git", "sudo", "su",
 	"shegang_config", "sgls", "sglsblk", "help", "exit", "quit", "bg", "term",
-	"nautilus", "gcc", "g++", "clang", "environment", NULL
+	"nautilus", "gcc", "g++", "clang", "environment"
 };
 
-extern int global_exit_code;
+extern int global_status_code;
 
 /**
  * Struct background task
@@ -234,7 +234,7 @@ void fuzzy_search_valid_command(char** args) {
 	int max_score = 0;
 	const char *text = "N/A";
 
-	for (size_t i = 0; i < sizeof(apps)/sizeof(*apps); i++) {
+	for (size_t i = 0; i < sizeof(apps)/sizeof(apps[0]); i++) {
 		int *score_tmp;
 		size_t score_len_tmp;
 		
@@ -269,22 +269,19 @@ int launch_task(char** args) {
 	pid_t pid;
 	int background = is_background_task(args);
 
-	global_exit_code = 0;
-
 	pid = fork();
 
 	if (pid < 0) {
 		print_message("Couldn't create child process!", ERROR);
 	} else if (pid == 0) {
-		if (execvp(args[0], args) == -1) {
+
+		if (execvp(args[0], args) != 0) {
 			print_message("Couldn't execute unknown command!", ERROR);
 			fuzzy_search_valid_command(args);
-			global_exit_code = -1;
-		} else {
-			global_exit_code = 0;
+			global_status_code = -1;
 		}
 		
-		return 1;
+		exit(-1);
 	} else {
 		if (background == 1) {
 			if (add_background_task(pid, args[0]) == -1) {
@@ -296,7 +293,7 @@ int launch_task(char** args) {
 			if (waitpid(pid, NULL, 0) == -1) {
 				if (errno != EINTR) {
 					print_message("Couldn't track the completion of the process", WARNING);
-					global_exit_code = -1;
+					global_status_code = -1;
 				}
 			}
 		}
diff --git a/src/features/autocomplete.c b/src/features/autocomplete.c
index 02d09b1..236d084 100644
--- a/src/features/autocomplete.c
+++ b/src/features/autocomplete.c
@@ -12,7 +12,28 @@
 
 extern ColorScheme* current_theme;
 
-extern const char *apps[];
+const char *progs[] = {
+	"print", "shegang_config", "exit", 
+	"then", "if", "fi", "else", "do", "while", "function",
+	"echo", "printf", "cat", "grep", "awk", "ls", 
+	"cd", "clear", "chmod", "chown", "rm", "mv", "cp",
+	"mkdir", "rmdir", "ln", "chgrp", "dd", "df", "du",
+	"mount", "nano", "vi", "vim", "micro", "nvim",
+	"find", "pwd", "locate", "zcat", "zless", "zmore",
+	"less", "ip", "ping", "kill", "pkill", "killall",
+	"ps", "top", "htop", "crontab", "systemctl",
+	"time", "tar", "gzip", "useradd", "userdel",
+	"usermod", "passwd", "head", "tail", "sort", "diff",
+	"date", "calc", "uname", "uptime", "apt", "dnf",
+	"pacman", "zypper", "nixpkg", "yum", "man", "touch",
+	"wget", "curl", "zip", "unzip", "find", "fzf", "unmount",
+	"sgls", "sglsblk", "help", "exit", 
+	"term", "reload", "history", "gapf",
+	"neofetch", "firefox", "alacritty", "mate-terminal",
+	"xfce4-terminal", "thunar", "git", "sudo", "su",
+	"shegang_config", "sgls", "sglsblk", "help", "exit", "quit", "bg", "term",
+	"nautilus", "gcc", "g++", "clang", "environment", NULL
+};
 
 /**
  * @brief      Word Auto Completer
@@ -21,7 +42,7 @@ extern const char *apps[];
  * @param[in]  word  The word
  */
 void word_completer(ic_completion_env_t* cenv, const char* word) {
-	ic_add_completions(cenv, word, apps);
+	ic_add_completions(cenv, word, progs);
 
 	if (strcmp(word, "sg") == 0) {
 		ic_add_completion(cenv, "sgls");
@@ -87,7 +108,7 @@ void highlighter(ic_highlight_env_t* henv, const char* input, void* arg) {
 								"unmount", "exec", "sudo", "su", "gcc", "clang",
 								"clang++", "g++", "ghc", "cabal", "stack",
 								"md5sum", "sha256sum", "mesg", "systemd-analyze",
-								"epm", "portage", NULL};
+								"epm", "portage", "neofetch", "screenfetch", "startx", NULL};
 		const char* builtin_utils[] = {"shegang_config", "sgls", "sglsblk", "help", "exit", 
 									"term", "reload", "history", "gapf", "quit", "bg", "term", "environment", NULL};
 		const char* operators[] = {"==", ">=", "<=", "!=", ">", "<", "+", "-", "*", "/", "&&", "||", NULL};
@@ -100,9 +121,6 @@ void highlighter(ic_highlight_env_t* henv, const char* input, void* arg) {
 		} else if ((tlen = ic_match_any_token(input, i, &ic_char_is_idletter, commands)) > 0) {
 			ic_highlight(henv, i, tlen, get_color(current_theme, "commands").hex);
 			i += tlen;
-		} else if ((tlen = ic_match_any_token(input, i, &ic_char_is_idletter, operators)) > 0) {
-			ic_highlight(henv, i, tlen, get_color(current_theme, "operators").hex);
-			i += tlen;
 		} else if ((tlen = ic_match_any_token(input, i, &ic_char_is_idletter, builtin_utils)) > 0) {
 			ic_highlight(henv, i, tlen, get_color(current_theme, "programs").hex);
 			i += tlen;
@@ -127,8 +145,13 @@ void highlighter(ic_highlight_env_t* henv, const char* input, void* arg) {
 			ic_highlight(henv, i, tlen, get_color(current_theme, "dark_gray").hex);
 			i += tlen;
 		} else {
-			ic_highlight(henv, i, 1, NULL);
-			i++;
+			if ((tlen = ic_match_any_token(input, i, &ic_char_is_nonwhite, operators)) > 0) {
+				ic_highlight(henv, i, tlen, get_color(current_theme, "operators").hex);
+				i += tlen;
+			} else {
+				ic_highlight(henv, i, 1, NULL);
+				i++;
+			}
 		}
 	}
 }
diff --git a/src/features/colors.c b/src/features/colors.c
index ac908c9..b72d0a9 100644
--- a/src/features/colors.c
+++ b/src/features/colors.c
@@ -45,7 +45,7 @@ extern char* username_color;
 extern char* pwd_color;
 extern char* curr_time_color;
 
-int global_exit_code = 0;
+int global_status_code = 0;
 
 /**
  * @brief      Gets the color by name.
@@ -170,7 +170,7 @@ void print_message(const char* message, int message_type) {
 
  @brief      Function for display shell prompt
 */
-void display_ps(void) {
+void display_ps(int status) {
 	pid_t uid = geteuid();
 	struct passwd *pw = getpwuid(uid);
 	char cwd[MAX_DIRECTORY_PATH];
@@ -194,8 +194,11 @@ void display_ps(void) {
 
 	printf("%s[%s%s%d:%d:%d%s%s] %s", GRAY, RESET, curr_time_color, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, RESET, GRAY, RESET);
 
-	if (global_exit_code != 0)
-		printf("%s%d ✘%s", RED, global_exit_code, RESET);
+	if (global_status_code != 0) {
+		printf("%s%d ✘%s", RED, global_status_code, RESET);
+	} else if (global_status_code == 0 && status != 1) {
+		printf("%s%d ✘%s", RED, status, RESET);
+	}
 
-	global_exit_code = 0;
+	global_status_code = 0;
 }
diff --git a/src/shegang.c b/src/shegang.c
index 3f811d2..5bed716 100644
--- a/src/shegang.c
+++ b/src/shegang.c
@@ -49,11 +49,14 @@ int main(int argc, char** argv) {
 
 	char* line;
 	char** args;
-	int status;
-
+	char** args_by_delims;
+	int status = 1;
 	int returned_value = 0;
-
 	int have_plugin = 0;
+	int num_tokens;
+	
+	const char* delims[] = {"&&", "||", ";"};
+	const int delims_len = sizeof(delims) / sizeof(delims[0]);
 
 	signal(SIGINT, kill_foreground_task);
 	signal(SIGCHLD, mark_ended_task);
@@ -80,7 +83,7 @@ int main(int argc, char** argv) {
 	}
 
 	do {
-		display_ps();
+		display_ps(status);
 
 		line = read_user_input();
 
@@ -92,12 +95,16 @@ int main(int argc, char** argv) {
 			myplugin.functions.mainloop(line);
 		}
 
-		args = split_into_tokens(line);
-		status = execute(args);
+		args_by_delims = split_by_delims(line, "&&||;", &num_tokens);
+
+		for (int i = 0; i < num_tokens; i++) {
+			args = split_into_tokens(args_by_delims[i]);
+			status = execute(args);
+		}
 
 		free(line);
 		free(args);
-	} while(status);
+	} while(status >= 1 || status < 0);
 
 	return 0;
 }
