commit 4c47f02034318ea9f134a0d02e852026422ae1d8
parent 28891a6cebb26b794fe6e5ce079f1c15982d2f42
Author: night0721 <[email protected]>
Date: Thu, 1 Feb 2024 19:02:30 +0000
handle signals, rename functions, history command, qol features, fix highlight highlighting full command
Diffstat:
9 files changed, 240 insertions(+), 122 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,4 +1,4 @@
-rush: rush.c color.c constants.h history.c
- gcc -o rush rush.c color.c history.c
+rush: rush.c color.c constants.h history.c commands.c
+ gcc -o rush rush.c color.c history.c commands.c
all:
rush
diff --git a/README.md b/README.md
@@ -17,15 +17,15 @@ $ ./rush
# Features
- Showing current time and directory with custom color
-- syntax highlighting on valid commands using ANSI colors
-- history navigation using up and down keys
+- Syntax highlighting on valid commands using ANSI colors
+- History navigation using up and down keys with history command
+- Built in commands
# Todo Features
- Pipe
- stdin, stdout, stderr redirect
- background jobs
- editing using left and right arrow keys
-- history command
- export command to setenv
- tab completion
diff --git a/color.c b/color.c
@@ -11,7 +11,7 @@ void color_text(char str[], const char *color) {
}
char *buf = malloc(size);
if (buf == NULL) {
- fprintf(stderr, "rush: Memory allocation failed\n");
+ fprintf(stderr, "rush: Error allocating memory\n");
exit(EXIT_FAILURE);
}
diff --git a/commands.c b/commands.c
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "commands.h"
+#include "history.h"
+
+// Function declarations for builtin commands
+int cd(char **args);
+int help(char **args);
+int quit(char **args);
+int history(char **args);
+
+// List of builtin commands' names
+char *builtin_cmds[] = {
+ "cd",
+ "help",
+ "exit",
+ "history"
+};
+
+int (*builtin_func[]) (char **) = {
+ &cd,
+ &help,
+ &quit, // cant name it exit as it is taken
+ &history
+};
+
+// number of built in commands
+int num_builtins() {
+ return sizeof(builtin_cmds) / sizeof(char *);
+}
+
+// change directory
+int cd(char **args) {
+ if (args[1] == NULL) {
+ char *home = getenv("HOME");
+ if (chdir(home) != 0) {
+ perror("rush");
+ }
+ } else {
+ if (chdir(args[1]) != 0) {
+ perror("rush");
+ }
+ }
+ return 1;
+}
+
+// show help menu
+int help(char **args) {
+ printf("rush v0.01-alpha\n");
+ printf("Built in commands:\n");
+
+ for (int i = 0; i < num_builtins(); i++) {
+ printf(" %s\n", builtin_cmds[i]);
+ }
+
+ printf("Use 'man' to read manual of programs\n");
+ printf("Licensed under GPL v3\n");
+ return 1;
+}
+
+int quit(char **args) {
+ return 0; // exit prompting loop, which also the shell
+}
+
+int history(char **args) {
+ char **history = get_all_history();
+
+ for (int i = 0; history[i] != NULL; ++i) {
+ printf("%s\n", history[i]);
+ free(history[i]);
+ }
+
+ free(history);
+ return 1;
+}
+
+bool is_builtin(char *command) {
+ for (int i = 0; i < num_builtins(); i++) {
+ if (strcmp(command, builtin_cmds[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// execute built in commands or launch commands and wait it to terminate, return 1 to keep shell running
+int execute(char **args) {
+ if (args[0] == NULL) { // An empty command was entered.
+ return 1;
+ }
+
+ for (int i = 0; i < num_builtins(); i++) {
+ if (strcmp(args[0], builtin_cmds[i]) == 0) {
+ return (*builtin_func[i])(args);
+ }
+ }
+
+ pid_t pid, wpid;
+ int status;
+
+ pid = fork();
+ if (pid == 0) {
+ // Child process
+ if (execvp(args[0], args) == -1) {
+ if (errno == ENOENT) {
+ fprintf(stderr, "rush: command not found: %s\n", args[0]);
+ }
+ }
+ exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ perror("Cannot fork");
+ } else {
+ // Parent process
+ do {
+ wpid = waitpid(pid, &status, WUNTRACED);
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ }
+
+ return 1;
+}
diff --git a/commands.h b/commands.h
@@ -0,0 +1,8 @@
+#ifndef COMMANDS_H_
+#define COMMANDS_H_
+
+int num_builtins();
+bool is_builtin(char *command);
+int execute(char **args);
+
+#endif
diff --git a/constants.h b/constants.h
@@ -3,7 +3,7 @@
#define HISTFILE ".rush_history" // history file name
#define TOK_BUFSIZE 64 // buffer size of each token
-#define RL_BUFSIZE 1024
+#define RL_BUFSIZE 1024 // size of each command
#define TOK_DELIM " \t\r\n\a" // delimiter for token
-
+#define MAX_HISTORY 8192 // maximum lines of reading history
#endif
diff --git a/history.c b/history.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <limits.h>
#include "constants.h"
@@ -89,3 +90,48 @@ char *read_command(int direction) {
fclose(history_file);
return last_nlf + 1; // return the string from the new line feed
}
+
+int is_duplicate(char **history, int line_count, char *line) {
+ for (int i = 0; i < line_count; ++i) {
+ if (strcmp(history[i], line) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+char **get_all_history() {
+ history_file = fopen(histfile_path, "r");
+ if (history_file == NULL) {
+ fprintf(stderr, "rush: Error opening history file\n");
+ exit(EXIT_FAILURE);
+ }
+
+ char **history = malloc(MAX_HISTORY * sizeof(char*));
+ if (history == NULL) {
+ fprintf(stderr, "rush: Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+ char buffer[RL_BUFSIZE]; // Adjust the buffer size as needed
+ int line_count = 0;
+
+ while (fgets(buffer, sizeof(buffer), history_file) != NULL) {
+ buffer[strcspn(buffer, "\n")] = '\0';
+ if (!is_duplicate(history, line_count, buffer)) {
+ history[line_count] = strdup(buffer);
+ if (history[line_count] == NULL) {
+ fprintf(stderr, "Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+ line_count++;
+ if (line_count >= MAX_HISTORY) {
+ fprintf(stderr, "Maximum number of lines reached.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ fclose(history_file);
+ history[line_count] = NULL;
+ return history;
+}
diff --git a/history.h b/history.h
@@ -4,5 +4,6 @@
void save_command_history(char *command);
void check_history_file();
char *read_command(int direction);
+char **get_all_history();
#endif
diff --git a/rush.c b/rush.c
@@ -1,4 +1,3 @@
-#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
@@ -7,112 +6,14 @@
#include <limits.h>
#include <time.h>
#include <stdbool.h>
-#include <errno.h>
#include "color.h"
#include "constants.h"
#include "history.h"
+#include "commands.h"
-/*
- * Function Declarations for builtin shell commands:
- */
-int rush_cd(char **args);
-int help(char **args);
-int rush_exit(char **args);
-
-
-/*
- * List of builtin commands, followed by their corresponding functions.
- */
-char *builtin_str[] = {
- "cd",
- "help",
- "exit"
-};
-
-int (*builtin_func[]) (char **) = {
- &rush_cd,
- &help,
- &rush_exit
-};
-
-int rush_num_builtins() {
- return sizeof(builtin_str) / sizeof(char *);
-}
-
-// change directory
-int rush_cd(char **args) {
- if (args[1] == NULL) {
- char *home = getenv("HOME");
- if (chdir(home) != 0) {
- perror("rush");
- }
- } else {
- if (chdir(args[1]) != 0) {
- perror("rush");
- }
- }
- return 1;
-}
-
-// show help menu
-int help(char **args) {
- printf("rush v0.01-alpha\n");
- printf("Built in commands:\n");
-
- for (int i = 0; i < rush_num_builtins(); i++) {
- printf(" %s\n", builtin_str[i]);
- }
-
- printf("Use 'man' to read manual of programs\n");
- printf("Licensed under GPL v3\n");
- return 1;
-}
-
-int rush_exit(char **args) {
- return 0; // exit prompting loop, which also the shell
-}
-
-// launch program and wait it to terminate, return 1 to continue running
-int rush_launch(char **args) {
- pid_t pid, wpid;
- int status;
-
- pid = fork();
- if (pid == 0) {
- // Child process
- if (execvp(args[0], args) == -1) {
- if (errno == ENOENT) {
- fprintf(stderr, "rush: command not found: %s\n", args[0]);
- }
- }
- exit(EXIT_FAILURE);
- } else if (pid < 0) {
- perror("Cannot fork");
- } else {
- // Parent process
- do {
- wpid = waitpid(pid, &status, WUNTRACED);
- } while (!WIFEXITED(status) && !WIFSIGNALED(status));
- }
-
- return 1;
-}
-
-// execute built in commands or launch commands, return 1 to keep shell running
-int rush_execute(char **args) {
- if (args[0] == NULL) {
- // An empty command was entered.
- return 1;
- }
-
- for (int i = 0; i < rush_num_builtins(); i++) {
- if (strcmp(args[0], builtin_str[i]) == 0) {
- return (*builtin_func[i])(args);
- }
- }
-
- return rush_launch(args);
+void quit_sig(int sig) {
+ return 0;
}
void change_terminal_attribute(int option) {
@@ -165,20 +66,24 @@ bool find_command(char **paths, char *command) {
if (access(current_path, X_OK) == 0) {
// command is executable
return true;
+ } else {
+ if (is_builtin(command)) {
+ return true;
+ }
}
paths++;
}
return false;
}
-char *rush_read_line(char **paths) {
+char *readline(char **paths) {
int bufsize = RL_BUFSIZE;
int position = 0;
char *buffer = malloc(sizeof(char) * bufsize);
int c;
if (!buffer) {
- fprintf(stderr, "rush: allocation error\n");
+ fprintf(stderr, "rush: Error allocating memory\n");
exit(EXIT_FAILURE);
}
@@ -186,6 +91,7 @@ char *rush_read_line(char **paths) {
while (1) {
c = getchar(); // read a character
int buf_len = strlen(buffer);
+ //printf("buflen %i\n", buf_len);
if (buf_len > 0) {
printf("\033[%dD", strlen(buffer)); // move cursor to the beginning
printf("\033[K"); // clear line to the right of cursor
@@ -240,21 +146,45 @@ char *rush_read_line(char **paths) {
}
}
char *cmd_part = strchr(buffer, ' ');
+ char *command_without_arg = NULL;
+ int cmd_len = 0;
bool valid;
+
if (cmd_part != NULL) {
- char *cmd = malloc(sizeof(char) * (cmd_part - buffer + 1));
+ cmd_len = cmd_part - buffer;
+ char *cmd = malloc(sizeof(char) * cmd_len + 1);
+ command_without_arg = malloc(sizeof(char) * cmd_len + 1);
+ if (cmd == NULL || command_without_arg == NULL) {
+ fprintf(stderr, "rush: Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
for (int i = 0; i < (cmd_part - buffer); i++) {
cmd[i] = buffer[i];
}
- cmd[cmd_part - buffer] = '\0';
+ strcpy(command_without_arg, cmd);
+ cmd[cmd_len] = '\0';
+ command_without_arg[cmd_len] = '\0';
valid = find_command(paths, cmd);
} else {
valid = find_command(paths, buffer);
}
+
if (valid) {
- printf("\x1b[38;2;000;255;000m%s\x1b[0m", buffer); // print green as valid command
+ if (command_without_arg != NULL) {
+ buffer += cmd_len;
+ printf("\x1b[38;2;137;180;250m%s\x1b[0m\x1b[38;2;255;255;255m%s\x1b[0m", command_without_arg, buffer); // print green as valid command, but only color the command, not the arguments
+ buffer -= cmd_len;
+ } else {
+ printf("\x1b[38;2;137;180;250m%s\x1b[0m", buffer); // print green as valid command
+ }
} else {
- printf("\x1b[38;2;255;000;000m%s\x1b[0m", buffer); // print red as sinvalid command
+ if (command_without_arg != NULL) {
+ buffer += cmd_len;
+ printf("\x1b[38;2;243;139;168m%s\x1b[0m\x1b[38;2;255;255;255m%s\x1b[0m", command_without_arg, buffer); // print green as valid command, but only color the command, not the arguments
+ buffer -= cmd_len;
+ } else {
+ printf("\x1b[38;2;243;139;168m%s\x1b[0m", buffer); // print red as sinvalid command
+ }
}
fflush(stdout);
@@ -263,7 +193,7 @@ char *rush_read_line(char **paths) {
bufsize += RL_BUFSIZE;
buffer = realloc(buffer, bufsize);
if (!buffer) {
- fprintf(stderr, "rush: allocation error\n");
+ fprintf(stderr, "rush: Error allocating memory\n");
exit(EXIT_FAILURE);
}
}
@@ -271,7 +201,7 @@ char *rush_read_line(char **paths) {
}
// split line into arguments
-char **rush_split_line(char *line) {
+char **argsplit(char *line) {
int bufsize = TOK_BUFSIZE, position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char *token;
@@ -297,6 +227,11 @@ char **rush_split_line(char *line) {
token = strtok(NULL, TOK_DELIM);
}
+ // makes ls and diff have color without user typing it
+ if (strcmp(tokens[0], "ls") == 0 || strcmp(tokens[0], "diff") == 0) {
+ tokens[position] = "--color=auto";
+ }
+ position++;
tokens[position] = NULL;
return tokens;
}
@@ -329,9 +264,9 @@ void command_loop(char **paths) {
printf("%s %s %s ", time, cwd, arrow);
- line = rush_read_line(paths);
- args = rush_split_line(line);
- status = rush_execute(args);
+ line = readline(paths);
+ args = argsplit(line);
+ status = execute(args);
free(line);
free(args);
@@ -339,9 +274,11 @@ void command_loop(char **paths) {
};
}
-
int main(int argc, char **argv) {
// setup
+ signal(SIGINT, quit_sig);
+ signal(SIGTERM, quit_sig);
+ signal(SIGQUIT, quit_sig);
check_history_file();
char **paths = setup_path_variable();
change_terminal_attribute(1); // turn off echoing and disabling getchar requires pressing enter key to return