Skip to content
Snippets Groups Projects
Commit eb1108b8 authored by Krasimir Balchev's avatar Krasimir Balchev
Browse files

Stage 7 finishing touches.

- Added history functionality from stages 5 and 6

- Fixed an out of array bounds bug in unalias function

- Added documentation in header file
parent e6dbfd11
No related branches found
No related tags found
No related merge requests found
......@@ -7,6 +7,7 @@
#include "shell.h"
#include <sys/wait.h>
Command history[MAX_HISTORY_LENGTH];
Alias aliases[MAX_ALIASES];
void run_shell() {
......@@ -17,6 +18,7 @@ void run_shell() {
// Set the current directory to HOME
if(getenv("HOME") != NULL) {
chdir(getenv("HOME"));
load_history();
}
// Keep original PATH
......@@ -31,17 +33,25 @@ void run_shell() {
// Alocate memory for the input & read user's input
input = malloc(MAX_INPUT_LENGTH * sizeof(char));
read_input(input);
// Tokenize to get the arguments
tokenize(input, arguments);
// Run command based on the user's input
status = run_command(arguments);
status = read_input(input);
if(status) {
// Tokenize to get the arguments
tokenize(input, arguments);
// Add input to history
if (arguments[0] != NULL && !is_history_invocation(arguments[0])) {
add_history(input);
}
// Run command based on the user's input
status = run_command(arguments);
}
// Free input and arguments
free(input);
free_array(arguments);
free_array(arguments);
printf("\n");
} while(status);
......@@ -51,14 +61,124 @@ void run_shell() {
setenv("PATH", originalPath, 1);
printf("PATH Restored: %s \n", getenv("PATH"));
}
if(getenv("HOME") != NULL) {
chdir(getenv("HOME"));
save_history();
}
}
void read_input(String input) {
int read_input(String input) {
if(fgets(input, MAX_INPUT_LENGTH, stdin) == NULL) {
free(input);
printf("\n");
exit(EXIT_STATUS);
return EXIT_STATUS;
}
// Remove trailing newline character
char *pos = strchr(input, '\n');
if (pos != NULL) {
*pos = '\0';
}
return SUCCESS_STATUS;
}
void add_history(String input) {
int counter;
int historySize = get_history_size();
if (historySize < MAX_HISTORY_LENGTH) {
counter = historySize;
historySize++;
}
else {
for (int i = 0; i < MAX_HISTORY_LENGTH; i++) {
if (history[i].number == 1) {
counter = i;
}
else {
history[i].number--;
}
}
free(history[counter].line);
}
history[counter].line = malloc(sizeof(char) * strlen(input) + 1);
history[counter].number = historySize;
strcpy(history[counter].line, input);
}
int execute_cmnd_from_history(String input, String arguments[]) {
if (arguments[1] == NULL) {
char *ptr;
int historySize = get_history_size();
long commandNum;
input++;
if (strcmp(input, "!") == 0) {
commandNum = historySize;
}
else {
for (int i = 0; i < strlen(input); i++) {
if (!((i == 0 && input[i] == '-') ||
(input[i] >= '0' && input[i] <= '9'))) {
printf("ERROR: ! only accapets positive or negative numbers after it\n");
return SUCCESS_STATUS;
}
}
commandNum = strtol(input, &ptr, 10);
if (commandNum < 0) {
commandNum += historySize + 1;
}
}
if (commandNum > 0 && commandNum <= historySize) {
int commandIndex = get_cmnd_from_history(commandNum);
if (commandIndex >= 0) {
free_array(arguments);
tokenize(history[commandIndex].line, arguments);
return run_command(arguments);
}
}
else {
printf("ERROR: Invalid command number!\n");
}
}
else {
printf("ERROR: Too many arguments. History invocations take no arguments\n");
}
return SUCCESS_STATUS;
}
Boolean is_history_invocation(String input) {
if (strcspn(input, "!") == 0 && strlen(input) > 1) {
return true;
}
return false;
}
int get_cmnd_from_history(long commandNum) {
int historySize = get_history_size();
for (int i = 0; i < historySize; i++) {
if (history[i].number == commandNum) {
return i;
}
}
return -1;
}
int get_history_size() {
int historySize = 0;
while (history[historySize].line != NULL && historySize < MAX_HISTORY_LENGTH) {
historySize++;
}
return historySize;
}
void tokenize(String input, String arguments[]) {
......@@ -66,23 +186,23 @@ void tokenize(String input, String arguments[]) {
String initialInput = malloc(sizeof(char) * strlen(input) + 1);
strcpy(initialInput, input);
int index = 0;
int index = 0;
String token = strtok(initialInput, TOKENIZE_DELIM);
while(token != NULL) {
// If token is an alias and the first argumet is not an alias invocation, replace token with actual command args.
int isAliasInvocation = index > 0 && strcmp(arguments[0], "alias") != 0 && strcmp(arguments[0], "unalias") != 0;
int aliasIndex = get_alias_value(token);
if (aliasIndex >= 0 && (index == 0 || isAliasInvocation)) {
index = replace_alias_with_cmnd(aliasIndex, arguments, index);
} else {
arguments[index] = malloc(sizeof(char) * strlen(token) + 1);
strcpy(arguments[index], token);
index++;
}
// If token is an alias and the first argumet is not an alias invocation, replace token with actual command args.
int isAliasInvocation = index > 0 && strcmp(arguments[0], "alias") != 0 && strcmp(arguments[0], "unalias") != 0;
int aliasIndex = get_alias_value(token);
if (aliasIndex >= 0 && (index == 0 || isAliasInvocation)) {
index = replace_alias_with_cmnd(aliasIndex, arguments, index);
} else {
arguments[index] = malloc(sizeof(char) * strlen(token) + 1);
strcpy(arguments[index], token);
index++;
}
token = strtok(NULL, TOKENIZE_DELIM);
token = strtok(NULL, TOKENIZE_DELIM);
}
// Set the last argument as NULL, so we can easily loop over the arguments if needed.
arguments[index] = NULL;
......@@ -90,95 +210,100 @@ void tokenize(String input, String arguments[]) {
}
void free_array(String array[]) {
int index = 0;
while (array[index] != NULL) {
free(array[index]);
array[index] = NULL;
index++;
}
int index = 0;
while (array[index] != NULL) {
free(array[index]);
array[index] = NULL;
index++;
}
}
int replace_alias_with_cmnd(int aliasIndex, String arguments[], int argsIndex) {
int index = 0;
while (aliases[aliasIndex].command[index]) {
arguments[argsIndex] = malloc(sizeof(char) * strlen(aliases[aliasIndex].command[index]) + 1);
while (aliases[aliasIndex].command[index] != NULL) {
arguments[argsIndex] = malloc(sizeof(char) * strlen(aliases[aliasIndex].command[index]) + 1);
strcpy(arguments[argsIndex], aliases[aliasIndex].command[index]);
index++;
argsIndex++;
}
index++;
argsIndex++;
}
return argsIndex;
return argsIndex;
}
int get_alias_value(String alias) {
int index = 0;
while (aliases[index].key != NULL) {
if (strcmp(aliases[index].key, alias) == 0) {
return index;
}
index++;
}
int index = 0;
while (aliases[index].key != NULL) {
if (strcmp(aliases[index].key, alias) == 0) {
return index;
}
index++;
}
return -1;
return -1;
}
void print_aliases() {
int i = 0;
// Print alises if they exist.
while(aliases[i].key != NULL) {
int j = 0;
printf("Alias `%s` set to `", aliases[i].key);
while (aliases[i].command[j] != NULL) {
if (j > 0) {
printf(" ");
}
printf("%s", aliases[i].command[j]);
j++;
int numberOfAliases = 0;
// Print alises if they exist
for (int i = 0; i < MAX_ALIASES; i++) {
if (aliases[i].key == NULL) {
continue;
}
int j = 0;
printf("Alias `%s` set to `", aliases[i].key);
while (aliases[i].command[j] != NULL) {
if (j > 0) {
printf(" ");
}
printf("%s", aliases[i].command[j]);
j++;
}
printf("`\n");
i++;
}
numberOfAliases++;
}
// If no aliases exist, print a message.
if(i < 1) {
if(numberOfAliases < 1) {
printf("ERROR: No aliases found\n");
}
}
int set_alias(String arguments[]) {
int status = -1;
int status = -1;
// Check if there is a place in the aliases array and store a new alias.
for(int i = 0; i < MAX_ALIASES; i++) {
if(aliases[i].key != NULL && strcmp(aliases[i].key, arguments[1]) == 0) {
status = 0;
free_array(aliases[i].command);
free_array(aliases[i].command);
} else if(aliases[i].key == NULL) {
status = 1;
aliases[i].key = malloc(sizeof(char) * MAX_INPUT_LENGTH);
strcpy(aliases[i].key, arguments[1]);
aliases[i].key = malloc(sizeof(char) * MAX_INPUT_LENGTH);
strcpy(aliases[i].key, arguments[1]);
}
if(status == -1) {
continue;
}
int commandArgIndex = 0;
int argsIndex = 2;
while(arguments[argsIndex] != NULL) {
int commandArgIndex = 0;
int argsIndex = 2;
while(arguments[argsIndex] != NULL && commandArgIndex < MAX_ALIASES_ARGUMENTS) {
aliases[i].command[commandArgIndex] = malloc(sizeof(char) * strlen(arguments[argsIndex]) + 1);
strcpy(aliases[i].command[commandArgIndex], arguments[argsIndex]);
strcpy(aliases[i].command[commandArgIndex], arguments[argsIndex]);
argsIndex++;
commandArgIndex++;
commandArgIndex++;
}
if(status == 1) {
printf("Alias `%s` was added\n", arguments[1]);
} else {
printf("Alias `%s` was overriden\n", arguments[1]);
}
printf("Alias `%s` was overriden\n", arguments[1]);
}
return SUCCESS_STATUS;
}
......@@ -187,7 +312,7 @@ int set_alias(String arguments[]) {
}
int execute_alias_cmnd(String arguments[]) {
// Count the arguments after the "alias" one.
// Count the arguments after the "alias" one.
int argumentsCount = 0;
while(arguments[argumentsCount + 1] != NULL) {
argumentsCount++;
......@@ -205,27 +330,23 @@ int execute_alias_cmnd(String arguments[]) {
break;
// Case 2: More than one argument, we need to overwrite or add another alias.
default:
return set_alias(arguments);
return set_alias(arguments);
}
return SUCCESS_STATUS;
}
int execute_unalias_cmnd(String arguments[]) {
if(arguments[1] != NULL) {
if(arguments[1] != NULL) {
Boolean isFound = false;
for(int i = 0; i < MAX_ALIASES; i++) {
if(aliases[i].key != NULL && strcmp(aliases[i].key, arguments[1]) == 0) {
free(aliases[i].key);
free_array(aliases[i].command);
for(int j = i; j < MAX_ALIASES; j++) {
aliases[j] = aliases[j + 1];
}
isFound = true;
break;
}
if(aliases[i].key != NULL && strcmp(aliases[i].key, arguments[1]) == 0) {
free(aliases[i].key);
free_array(aliases[i].command);
aliases[i].key = NULL;
isFound = true;
break;
}
}
if(isFound) {
......@@ -241,7 +362,7 @@ int execute_unalias_cmnd(String arguments[]) {
}
int execute_change_dir_cmnd(String arguments[]) {
// If no arguments are supplied, return to the home directory
// If no arguments are supplied, return to the home directory
if(arguments[1] == NULL) {
chdir(getenv("HOME"));
} else {
......@@ -256,7 +377,7 @@ int execute_change_dir_cmnd(String arguments[]) {
}
int execute_getpath_cmnd(String arguments[]) {
if(arguments[1] == NULL) {
if(arguments[1] == NULL) {
printf("%s\n", getenv("PATH"));
} else {
printf("ERROR: Too many arguments. Command getpath takes no arguments\n");
......@@ -266,7 +387,7 @@ int execute_getpath_cmnd(String arguments[]) {
}
int execute_setpath_cmnd(String arguments[]) {
if(arguments[1] == NULL) {
if(arguments[1] == NULL) {
printf("ERROR: Invalid arguments. Command setpath requires one argument\n");
} else if (arguments[2] != NULL) {
printf("ERROR: Too many arguments. Command setpath accepts only one argument\n");
......@@ -280,32 +401,40 @@ int execute_setpath_cmnd(String arguments[]) {
int run_command(String arguments[]) {
if(arguments[0] != NULL) {
if(strcmp(arguments[0], "exit") == 0) {
return EXIT_STATUS;
}
if(strcmp(arguments[0], "exit") == 0) {
return EXIT_STATUS;
}
if(strcmp(arguments[0], "getpath") == 0) {
return execute_getpath_cmnd(arguments);
}
if(strcmp(arguments[0], "getpath") == 0) {
return execute_getpath_cmnd(arguments);
}
if(strcmp(arguments[0], "setpath") == 0) {
return execute_setpath_cmnd(arguments);
}
if(strcmp(arguments[0], "setpath") == 0) {
return execute_setpath_cmnd(arguments);
}
if(strcmp(arguments[0], "cd") == 0) {
return execute_change_dir_cmnd(arguments);
}
if(strcmp(arguments[0], "cd") == 0) {
return execute_change_dir_cmnd(arguments);
}
if(strcmp(arguments[0], "alias") == 0) {
return execute_alias_cmnd(arguments);
}
if (is_history_invocation(arguments[0])) {
return execute_cmnd_from_history(arguments[0], arguments);
}
if(strcmp(arguments[0], "unalias") == 0) {
return execute_unalias_cmnd(arguments);
}
// If the command was not found above, fork a child process and run the command there.
return execute_system_commands(arguments);
if (strcmp(arguments[0], "history") == 0) {
return print_history(arguments);
}
if(strcmp(arguments[0], "alias") == 0) {
return execute_alias_cmnd(arguments);
}
if(strcmp(arguments[0], "unalias") == 0) {
return execute_unalias_cmnd(arguments);
}
// If the command was not found above, fork a child process and run the command there.
return execute_system_commands(arguments);
}
return SUCCESS_STATUS;
......@@ -318,6 +447,73 @@ void get_directory(char directory[]) {
}
int print_history(String arguments[]) {
if (arguments[1] == NULL) {
int index = 0;
int commandCounter = 0;
int historySize = get_history_size();
while (commandCounter < historySize && history[index].number != 1) {
index++;
commandCounter++;
}
commandCounter = 1;
while (commandCounter <= historySize) {
printf("%3d %s\n", history[index].number, history[index].line);
index = (index + 1) % MAX_HISTORY_LENGTH;
commandCounter++;
}
} else {
printf("ERROR: Too many arguments. Command history takes no arguments\n");
}
return SUCCESS_STATUS;
}
void load_history() {
FILE* fp = fopen(HISTORY_FILE_NAME, "r");
if (fp != NULL) {
String line = malloc(sizeof(char) * MAX_INPUT_LENGTH);
int index = 0;
while (fgets(line, MAX_INPUT_LENGTH, fp) != NULL && index < MAX_HISTORY_LENGTH) {
// Remove trailing newlie character
char *pos = strchr(line, '\n');
if (pos != NULL) {
*pos = '\0';
}
add_history(line);
history[index].number = index + 1;
index++;
}
free(line);
fclose(fp);
}
}
void save_history() {
FILE* fp = fopen(HISTORY_FILE_NAME, "w");
int index = 0;
int commandCounter = 0;
int historySize = get_history_size();
while (commandCounter < historySize && history[index].number != 1) {
index++;
commandCounter++;
}
commandCounter = 0;
while (commandCounter < historySize) {
fprintf(fp, "%s\n", history[index].line);
free(history[index].line);
index = (index + 1) % MAX_HISTORY_LENGTH;
commandCounter++;
}
fclose(fp);
}
int execute_system_commands(String arguments[]) {
int status;
pid_t pid, child_pid;
......
/* shell.h */
#define EXIT_STATUS 0
#define SUCCESS_STATUS 1
#define MAX_HISTORY_LENGTH 20
#define MAX_INPUT_LENGTH 512
#define MAX_PATH_LENGTH 512
#define TOKENIZE_DELIM " \t\n|><&;"
#define HISTORY_FILE_NAME ".hist_list"
#define MAX_ALIASES 10
#define MAX_ALIASES_ARGUMENTS 10
/*
* Define a string as a char pointer
/**
* Define a string as a char pointer
*/
typedef char* String;
/*
* Define boolean
/**
* Define boolean
*/
typedef enum {
false = 0,
true = 1
false = 0,
true = 1
} Boolean;
/**
* Define command history struct
*/
typedef struct {
String line;
int number;
} Command;
/**
* Define alias struct
*/
typedef struct {
String key;
String command[MAX_ALIASES_ARGUMENTS];
String key;
String command[MAX_ALIASES_ARGUMENTS];
} Alias;
/**
* Print the arguments.
* Print the arguments
*/
void print_arguments(String arguments[]);
/**
* Run the shell program
* Get the current working directory
*/
void get_directory(char directory[]);
/*
* Execute change directory command.
/**
* Execute change directory command
*/
int execute_change_dir_cmnd(String arguments[]);
/*
* Execute set path command.
/**
* Execute set path command
*/
int execute_setpath_cmnd(String arguments[]);
/*
* Execute get path command.
/**
* Execute get path command
*/
int execute_getpath_cmnd(String arguments[]);
/**
* Run the shell program
* Execute system commands
*/
int execute_system_commands(String argruments[]);
/**
* Get size of history array
*/
int get_history_size();
/**
* Prints history
*/
int print_history(String arguments[]);
/**
* Run the shell program
*/
int run_command(String argruments[]);
/*
* Print aliases.
/**
* Print aliases.
*/
void print_aliases();
/*
* Set new alias in aliases array.
/**
* Set new alias in aliases array.
*/
int set_alias(String arguments[]);
/*
* Execute alias command based on number of arguments given.
/**
* Execute alias command based on number of arguments given.
*/
int execute_alias_cmnd(String arguments[]);
/*
* Execute unalias command.
/**
* Execute unalias command.
*/
int execute_unalias_cmnd(String arguments[]);
/*
* Replace alias in arguments with actual command.
* Returns the next free index in arguments array.
/**
* Replace alias in arguments with actual command.
* Returns the next free index in arguments array.
*/
int replace_alias_with_cmnd(int aliasIndex, String arguments[], int argsIndex);
/*
* Get index of alias from alias key.
/**
* Get index of alias from alias key.
*/
int get_alias_value(String alias);
/*
* Frees the allocoted memory in the arguments array.
/**
* Frees the allocoted memory in the arguments array.
*/
void free_array(String array[]);
/**
* Run the shell program
* Splits the input into a String array
*/
void tokenize(String input, String argruments[]);
/**
* Run the shell program
* Get index of command in history array
*/
int get_cmnd_from_history(long commandNum);
/**
* Execute a command saved in history
*/
int execute_cmnd_from_history(String input, String arguments[]);
/**
* Add input to history
*/
void add_history(String input);
/**
* Is the string a history invocation
*/
Boolean is_history_invocation(String input);
/**
* Load history from file
*/
void load_history();
/**
* Save history into file
*/
void save_history();
/**
* Read input
*/
void read_input();
int read_input();
/**
* Run the shell program
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment