commit 0a8d3ca51c03ed6502602e3e0082841fa99cc455
parent 9b27891d43dbd2f5ee646456efbf3bf309c60f11
Author: night0721 <[email protected]>
Date: Thu, 8 Feb 2024 19:49:11 +0000
history navigation by !!
Diffstat:
M | commands.c | | | 20 | ++++++++++++++------ |
M | history.c | | | 83 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | history.h | | | 4 | ++-- |
M | rush.c | | | 188 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | rush.h | | | 2 | +- |
5 files changed, 180 insertions(+), 117 deletions(-)
diff --git a/commands.c b/commands.c
@@ -6,11 +6,12 @@
#include <errno.h>
#include <sys/wait.h>
-#include "commands.h"
#include "constants.h"
#include "history.h"
#include "rush.h"
+int execute(char **args);
+
// Function declarations for builtin commands
int cd(char **args);
int help(char **args);
@@ -81,7 +82,7 @@ int quit(char **args) {
}
int history(char **args) {
- char **history = get_all_history();
+ char **history = get_all_history(true);
for (int i = 0; history[i] != NULL; ++i) {
printf("%s\n", history[i]);
@@ -158,17 +159,24 @@ int execute(char **args) {
return 1;
}
+ save_command_history(args); // save command to history file
+
// prioritize builtin commands
for (int i = 0; i < num_builtins(); i++) {
if (strcmp(args[0], builtin_cmds[i]) == 0) {
return (*builtin_func[i])(args);
}
}
- /*
- while (*args) {
-
+
+ int num_arg = 0;
+
+ while (*args != NULL) {
+ num_arg++; // count number of args
+ args++;
}
- */
+
+ args -= num_arg;
+
pid_t pid;
int status;
diff --git a/history.c b/history.c
@@ -1,9 +1,11 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <linux/limits.h>
+#include "history.h"
#include "constants.h"
FILE *history_file;
@@ -26,8 +28,8 @@ void check_history_file() {
int path_len = env_home_len + histfilename_len + 2; // 2 for slash and null byte
histfile_path = malloc(sizeof(char) * path_len);
if (histfile_path == NULL) {
- fprintf(stderr, "rush: Error allocating memory\n");
- exit(EXIT_FAILURE);
+ fprintf(stderr, "rush: Error allocating memory\n");
+ exit(EXIT_FAILURE);
}
histfile_path[0] = '\0'; // initialise string
// concatenate home and history file name to a path
@@ -45,36 +47,26 @@ void check_history_file() {
}
}
-void save_command_history(char *command) {
+void save_command_history(char **args) {
history_file = fopen(histfile_path, "a+");
if (history_file == NULL) {
fprintf(stderr, "rush: Error opening history file\n");
exit(EXIT_FAILURE);
}
- int cmd_len = strlen(command);
- command[cmd_len] = '\n'; // put new line feed to split commands
+ char cmd[RL_BUFSIZE];
+ cmd[0] = '\0';
+ for (int i = 0; args[i] != NULL; ++i) {
+ strcat(cmd, args[i]);
+ strcat(cmd, " ");
+ }
+ int cmd_len = strlen(cmd);
+ cmd[cmd_len] = '\n'; // put new line feed to split commands
// ptr to first obj, size of each obj, number of obj, file ptr
- fwrite(command, sizeof(char), cmd_len + 1, history_file);
+ fwrite(cmd, sizeof(char), cmd_len + 1, history_file);
fclose(history_file);
}
char *read_command(int direction) {
- history_file = fopen(histfile_path, "rb"); // read binary mode
- if (history_file == NULL) {
- fprintf(stderr, "rush: Error opening history file\n");
- exit(EXIT_FAILURE);
- }
- // normal bufsize is 1024, we serach for 1025 bytes for new line feed
- int search_len = RL_BUFSIZE + 1;
- char search[search_len];
- fseek(history_file, -search_len, SEEK_END); // go back 1025 characters from end of file
- int count = fread(search, 1, search_len - 1, history_file); // try to read 1025 characters from file, returning count number of bytes
- search[count] = '\0';
- char *last_nlf = strrchr(search, '\n'); // locate last occurence of \n in a searching string
- if (last_nlf == NULL) {
- // no history
- return NULL;
- }
if (direction == 1) { // up
cmd_count++;
} else { // down
@@ -84,24 +76,18 @@ char *read_command(int direction) {
cmd_count--;
}
}
- for (int i = 0; i < cmd_count; i++) {
- search[last_nlf - search] = '\0'; // terminate string earlier to find second last \n, search points to first char and last_nlf is last \n, difference is the index of \n
- last_nlf = strrchr(search, '\n'); // call strrchr 2 times to get second last new line feed in search string as every life is new line feed
- if ((last_nlf - search) == (strchr(search, '\n') - search)) {
- // check if the first \n is the last \n we searching for, if yes it is first command
- cmd_count--;
- search[last_nlf - search] = '\0'; // terminate string earlier to find second last \n, search points to first char and last_nlf is last \n, difference is the index of \n
- char *first_cmd = malloc(sizeof(char) * (last_nlf - search) + 1);
- if (first_cmd == NULL) {
- fprintf(stderr, "rush: Error allocating memory\n");
- exit(EXIT_FAILURE);
- }
- strcpy(first_cmd, search);
- return first_cmd;
- }
+ char **history = get_all_history(false);
+ int num_history = 0;
+ while (*history != NULL) {
+ num_history++;
+ history++;
}
- fclose(history_file);
- return last_nlf + 1; // return the string from the new line feed
+ if (cmd_count > num_history) {
+ cmd_count = num_history;
+ return NULL;
+ }
+ history -= num_history;
+ return history[num_history - cmd_count];
}
int is_duplicate(char **history, int line_count, char *line) {
@@ -113,7 +99,7 @@ int is_duplicate(char **history, int line_count, char *line) {
return 0;
}
-char **get_all_history() {
+char **get_all_history(bool check) {
history_file = fopen(histfile_path, "r");
if (history_file == NULL) {
fprintf(stderr, "rush: Error opening history file\n");
@@ -130,7 +116,20 @@ char **get_all_history() {
while (fgets(buffer, sizeof(buffer), history_file) != NULL) {
buffer[strcspn(buffer, "\n")] = '\0';
- if (!is_duplicate(history, line_count, buffer)) {
+ if (check) {
+ 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);
+ }
+ }
+ } else {
history[line_count] = strdup(buffer);
if (history[line_count] == NULL) {
fprintf(stderr, "Error allocating memory\n");
@@ -141,7 +140,7 @@ char **get_all_history() {
fprintf(stderr, "Maximum number of lines reached.\n");
exit(EXIT_FAILURE);
}
- }
+ }
}
fclose(history_file);
diff --git a/history.h b/history.h
@@ -1,9 +1,9 @@
#ifndef HISTORY_H_
#define HISTORY_H_
-void save_command_history(char *command);
+void save_command_history(char **args);
void check_history_file();
char *read_command(int direction);
-char **get_all_history();
+char **get_all_history(bool check);
#endif
diff --git a/rush.c b/rush.c
@@ -69,7 +69,7 @@ char **setup_path_variable() {
}
bool find_command(char **paths, char *command) {
- if (strcmp(command, "") == 0) {
+ if (strncmp(command, "", 1) == 0) {
return false;
}
while (*paths != NULL) {
@@ -97,6 +97,56 @@ void shiftright(int chars) {
printf("\033[%dC", chars);
}
+void clearline() {
+ printf("\033[K"); // clear line to the right of cursor
+}
+
+void highlight(char *buffer, char **paths) {
+ char *cmd_part = strchr(buffer, ' ');
+ char *command_without_arg = NULL;
+ int cmd_len = 0;
+ bool valid;
+
+ if (cmd_part != NULL) {
+ 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];
+ }
+ strcpy(command_without_arg, cmd);
+ cmd[cmd_len] = '\0';
+ command_without_arg[cmd_len] = '\0';
+ valid = find_command(paths, cmd);
+ free(cmd);
+ } else {
+ valid = find_command(paths, buffer);
+ }
+
+ if (valid) {
+ 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 {
+ 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 invalid command
+ }
+ }
+ free(command_without_arg);
+}
+
char *readline(char **paths) {
int bufsize = RL_BUFSIZE;
int position = 0;
@@ -106,6 +156,7 @@ char *readline(char **paths) {
bool backspaced = false;
bool navigated = false;
bool insertatmiddle = false;
+ bool replaced = false;
if (!buffer) {
fprintf(stderr, "rush: Error allocating memory\n");
exit(EXIT_FAILURE);
@@ -125,15 +176,39 @@ char *readline(char **paths) {
if (buf_len == 0) {
break;
}
- buffer[buf_len] = '\0';
- // clear all characters after the command
- for (int start = buf_len + 1; buffer[start] != '\0'; start++) {
- buffer[start] = '\0';
+ // check if command includes !!
+ if (strstr(buffer, "!!") != NULL) {
+ char *last_command = read_command(1);
+ if (last_command != NULL) {
+ // replace !! with the last command
+ char *replace = strstr(buffer, "!!");
+ int replace_len = strlen(replace);
+ int last_command_len = strlen(last_command);
+ int buffer_len = strlen(buffer);
+ if (last_command_len > replace_len) {
+ buffer = realloc(buffer, buffer_len + last_command_len - replace_len + 1);
+ if (!buffer) {
+ fprintf(stderr, "rush: Error allocating memory\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ memmove(replace + last_command_len, replace + replace_len, buffer_len - (replace - buffer) - replace_len + 1);
+ memcpy(replace, last_command, last_command_len);
+ position += last_command_len - replace_len;
+ shiftright(last_command_len - replace_len);
+ replaced = true;
+ break;
+ }
+ } else {
+ buffer[buf_len] = '\0';
+ // clear all characters after the command
+ for (int start = buf_len + 1; buffer[start] != '\0'; start++) {
+ buffer[start] = '\0';
+ }
+ printf("\n"); // give space for response
+ position = 0;
+ return buffer;
}
- printf("\n"); // give space for response
- save_command_history(buffer);
- position = 0;
- return buffer;
}
case 127: // backspace
if (buf_len >= 1) {
@@ -154,16 +229,16 @@ char *readline(char **paths) {
char *last_command = read_command(1);
if (last_command != NULL) {
strcpy(buffer, last_command);
- navigated = true;
}
+ navigated = true;
moved = false;
break;
} else if (arrow_key == 66) { // down
char *last_command = read_command(0);
if (last_command != NULL) {
strcpy(buffer, last_command);
- navigated = true;
}
+ navigated = true;
moved = false;
break;
} else if (arrow_key == 67) { // right
@@ -200,18 +275,28 @@ char *readline(char **paths) {
position++;
}
}
+
+ buf_len = strlen(buffer);
+
+ if (replaced) {
+ shiftleft(buf_len);
+ clearline();
+ }
+
if (navigated && buf_len >= 1) {
- if (position != 0) {
- shiftleft(buf_len); // move cursor to the beginning
- printf("\033[K"); // clear line to the right of cursor
+ if (position > 0) {
+ shiftleft(position); // move cursor to the beginning
+ clearline();
}
+ position = buf_len;
}
- buf_len = strlen(buffer);
+
if (moved) {
moved = false;
continue;
}
- if (!navigated) {
+
+ if (!navigated && !replaced) {
if (position != buf_len) {
// not at normal place
if (backspaced) {
@@ -234,54 +319,13 @@ char *readline(char **paths) {
shiftleft(1);
}
}
- printf("\033[K"); // clear line to the right of cursor
+ clearline();
} else {
navigated = false;
}
- char *cmd_part = strchr(buffer, ' ');
- char *command_without_arg = NULL;
- int cmd_len = 0;
- bool valid;
-
- if (cmd_part != NULL) {
- 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];
- }
- strcpy(command_without_arg, cmd);
- cmd[cmd_len] = '\0';
- command_without_arg[cmd_len] = '\0';
- valid = find_command(paths, cmd);
- free(cmd);
- } else {
- valid = find_command(paths, buffer);
- }
- if (valid) {
- 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 {
- 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 invalid command
- }
- }
- free(command_without_arg);
-
+ highlight(buffer, paths);
+
if (backspaced) {
if (buf_len != position) {
shiftleft(buf_len - position);
@@ -291,8 +335,11 @@ char *readline(char **paths) {
if (insertatmiddle) {
shiftleft(buf_len - position); // move cursor back to where it was
insertatmiddle = false;
-
}
+ if (replaced) {
+ replaced = false;
+ }
+
// If we have exceeded the buffer, reallocate.
if ((buf_len + 1) >= bufsize) {
bufsize += RL_BUFSIZE;
@@ -332,15 +379,23 @@ char **argsplit(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;
}
+char **modifyargs(char **args) {
+ int num_arg = 0;
+
+ // makes ls and diff have color without user typing it
+ if (strncmp(args[0], "ls", 2) == 0 || strncmp(args[0], "diff", 4) == 0) {
+ args[num_arg] = "--color=auto";
+ num_arg++;
+ args[num_arg] = NULL;
+ }
+
+ return args;
+}
+
// continously prompt for command and execute it
void command_loop(char **paths) {
char *line;
@@ -371,6 +426,7 @@ void command_loop(char **paths) {
line = readline(paths);
args = argsplit(line);
+ args = modifyargs(args);
status = execute(args);
free(line);
diff --git a/rush.h b/rush.h
@@ -1,6 +1,6 @@
#ifndef RUSH_H_
#define RUSH_H_
-char **argsplit(char * line);
+char **argsplit(char *line);
#endif