This commit is contained in:
s492459 2025-01-04 18:11:07 +01:00
parent 626e12097d
commit ccb7d7e118

175
main.c
View File

@ -5,23 +5,142 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#define MAX_SIZE 1024
#define HISTORY_SIZE 5
// int LogCommandHistory(char* buf, char** command_log, int n) {
// command_log[n] = buf;
// }
// RAW TERMINAL INPUT / LOGGING HISTORY IMPLEMENTATION -----------
void PrintCurrentDir() { // pwd
// Global variable for termios
struct termios orig_termios;
// Termios functions
void EnableRawMode(struct termios* orig_termios) {
struct termios raw = *orig_termios;
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
void DisableRawMode(struct termios* orig_termios) {
tcsetattr(STDIN_FILENO, TCSAFLUSH, orig_termios);
}
void DisableRawModeWrapper() {
DisableRawMode(&orig_termios);
}
int LogCommandHistory(char* buf, char** command_log, int* n) {
int index = *n % HISTORY_SIZE;
int prev_index = (*n == 0) ? HISTORY_SIZE - 1 : (*n - 1) % HISTORY_SIZE;
if (command_log[prev_index] != NULL && strcmp(buf, command_log[prev_index]) == 0) {
return 0;
}
if (command_log[index] != NULL) {
free(command_log[index]);
}
command_log[index] = strdup(buf);
(*n)++;
return 0;
}
void FreeCommandHistory(char** command_log, int n) {
for (int i = 0; i < n; i++) {
free(command_log[i]);
}
}
void ClearBufferContent(int length) { // Function to clear buffer
for (int i = 0; i < length; i++) {
printf("\b \b");
}
}
// Get raw input from terminal + arrow key history navigation
void GetRawInput(char* buf, char** command_log, int n) {
int index = n % HISTORY_SIZE;
int history_counter = 0;
int pos = 0;
char c;
while (1) {
c = getchar();
if (c == '\n') { // Enter key
buf[pos] = '\0';
printf("\n");
return;
} else if (c == 127 || c == '\b') { // Backspace
if (pos > 0) {
pos--;
buf[pos] = '\0';
printf("\b \b");
}
} else if (c == '\033') { // Inputting arrow keys
getchar(); // Skip '[' character
char arrow = getchar();
if (arrow == 'A') { // Up arrow
int prev_index = (index == 0) ? HISTORY_SIZE - 1 : (index - 1) % HISTORY_SIZE;
if (command_log[prev_index] != NULL) {
ClearBufferContent(pos); // Clear current buffer
index = (index == 0) ? HISTORY_SIZE - 1 : index - 1;
history_counter++;
strcpy(buf, command_log[index]);
printf("%s", buf);
pos = strlen(buf);
}
}
else if (arrow == 'B') { // Down arrow
if (history_counter > 0) {
index = (index + 1) % HISTORY_SIZE;
history_counter--;
ClearBufferContent(pos);
if (history_counter == 0) {
buf[0] = '\0'; // Clear buffer
pos = 0;
} else {
strcpy(buf, command_log[index]);
printf("%s", buf);
pos = strlen(buf);
}
}
}
} else { // Regular character
if (pos < MAX_SIZE - 1) {
buf[pos++] = c;
buf[pos] = '\0';
printf("%c", c);
}
}
}
}
// MAIN MICROSHELL FUNCTIONS -----------
char* GetCurrentUserAndDir() { // return current user + working directory
char cwd[MAX_SIZE];
if (getcwd(cwd, sizeof(cwd)) == NULL) {
perror("getcwd() error");
return NULL;
}
char* user = getenv("USER");
if (user == NULL) {
perror("getenv() error");
return NULL;
}
printf("[%s@%s] $ ", user, cwd);
size_t result_size = strlen(user) + strlen(cwd) + 7; // Allocate memory (include extra space for format characters)
char* result = (char*)malloc(result_size);
snprintf(result, result_size, "[%s@%s] $ ", user, cwd); // Format the prompt string
return result;
}
int ChangeDirectory(char** args) { // cd
@ -38,14 +157,12 @@ int ChangeDirectory(char** args) { // cd
}
void PrintHelp() { // help
printf("-------------------------------------------------");
printf("-------------------------------------------------\n");
printf("C Microshell implementation by Maciej Życzyński\n");
printf("\"exit\" - exit the program");
printf("\"help\" - display this menu");
printf("Other bash commands work accordingly in child processes");
printf("Currently logged user / working directory:");
PrintCurrentDir();
printf("-------------------------------------------------");
printf("\"exit\" - exit the program\n");
printf("\"help\" - display this menu\n");
printf("Other bash commands work accordingly in child processes\n");
printf("-------------------------------------------------\n");
}
int HandleBuiltInCommands(char* command, char** args) { // Manual implementation of commands unsupported by execvp()
@ -71,10 +188,9 @@ int HandleCommand(char* command, char** args) { // Execute commands supported by
return 0;
}
int ParseCommand(char* buf, char** command, char** args) {
int ParseCommand(char* buf, char** command, char** args) { // Parse input string into command and array of arguments
char buf_cp[MAX_SIZE];
strcpy(buf_cp, buf); // Leave the original buffer (important for command history implementation)
buf_cp[strlen(buf_cp) - 1] = '\0'; // Remove newline from input string
char* token = strtok(buf_cp, " ");
int tok_count = 0;
@ -94,25 +210,39 @@ int ParseCommand(char* buf, char** command, char** args) {
return 0;
}
int main() {
char buf[MAX_SIZE];
char* command_log[MAX_SIZE];
char* command;
char* args[MAX_SIZE];
int n = 0;
char* command_log[HISTORY_SIZE] = {NULL};
// Save original terminal settings and restore them on exit
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) {
perror("tcgetattr() error");
return 1;
}
atexit(DisableRawModeWrapper); // call DisableRawModeWrapper when program exits
while (1) {
PrintCurrentDir();
char* prompt = GetCurrentUserAndDir(); // Display prompt
if (prompt != NULL) {
printf("%s", prompt);
free(prompt);
}
char* command;
char* args[MAX_SIZE];
fgets(buf, sizeof(buf), stdin);
EnableRawMode(&orig_termios);
GetRawInput(buf, command_log, n);
DisableRawMode(&orig_termios);
if (ParseCommand(buf, &command, args) < 0) {
if (ParseCommand(buf, &command, args) < 0) { // Parse buffer input
printf("Invalid command\n");
continue;
}
// LogCommandHistory(buf, &command_log, n);
// n++;
LogCommandHistory(buf, command_log, &n); // Add buffer to logging history
int handler_value = HandleBuiltInCommands(command, args);
if (handler_value == 2) { // Exit command
@ -140,5 +270,6 @@ int main() {
}
}
FreeCommandHistory(command_log, n);
return 0;
}