Last active
March 12, 2024 08:44
-
-
Save TanmayPatil105/010550ec9bac2a91b8705deee95337b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <fcntl.h> | |
#include <signal.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
/* | |
** determine type of redirection | |
*/ | |
typedef enum | |
{ | |
REDIRECT_INPUT, | |
REDIRECT_OUTPUT, | |
REDIRECT_NONE | |
} Direction; | |
/* | |
** data structure to store history | |
*/ | |
typedef struct History | |
{ | |
char *command; | |
struct History *next; | |
} History; | |
/* | |
** max input length and prompt colors | |
*/ | |
#define MAX 1024 | |
#define GREEN "\033[1;32m" | |
#define WHITE "\e[0;37m" | |
#define BLUE "\033[1;34m" | |
/* | |
** length of arguments and nuumber of multiple pipes | |
*/ | |
int ARGS_LENGTH; | |
int PIPE_LENGTH; | |
/* | |
** finds current working directory | |
*/ | |
char *dir(); | |
/* | |
** prints prompt | |
*/ | |
void print_prompt(char *, bool); | |
/* | |
** reads commands as input | |
*/ | |
int read_command(char[], History **); | |
/* | |
** changes prompt value | |
*/ | |
bool change_prompt(char[]); | |
/* | |
** hanldes prompt on change in variable values | |
*/ | |
void handle_prompt(char *, char **, bool *); | |
/* | |
** checks for prompt value for assigning cwd | |
*/ | |
void check_prompt(char **, bool); | |
/* | |
** check if any value is assigned to PATH | |
*/ | |
bool check_if_path(char *); | |
/* | |
** sets path to assigned value | |
*/ | |
void set_path(char *, char[]); | |
/* | |
** parses user commands | |
*/ | |
char **parse_arg(char *, Direction *, char **); | |
/* | |
** handles cd options | |
*/ | |
void handle_cd(char **, char **, bool); | |
/* | |
** executes user commands | |
*/ | |
void execute(char **, char *, Direction, char *); | |
/* | |
** store command to history | |
*/ | |
void add_history(History **, char *); | |
/* | |
** print history command | |
*/ | |
void print_history(History *); | |
void sig_handler(int signum) | |
{ | |
printf("\n"); | |
kill(getppid(), SIGINT); | |
} | |
void sig_stop_handler(int signum) | |
{ | |
printf("\n"); | |
kill(getppid(), SIGSTOP); | |
} | |
int main() | |
{ | |
char input[MAX]; | |
char *cwd = dir(); | |
char PATH[MAX] = | |
"/usr/local/sbin:/usr/" | |
"local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/" | |
"snap/bin:/usr/local/go/bin:/usr/local/go/bin"; | |
bool PromptFlag = false; | |
Direction direction_type = REDIRECT_NONE; | |
char *file_name; | |
History *head = NULL; | |
signal(SIGINT, sig_handler); | |
signal(SIGTSTP, sig_stop_handler); | |
while (1) | |
{ | |
print_prompt(cwd, PromptFlag); | |
if (read_command(input, &head) == 0) | |
{ | |
return 0; | |
} | |
if (strcmp(input, "history") == 0) | |
{ | |
print_history(head); | |
continue; | |
} | |
if (change_prompt(input)) | |
{ | |
handle_prompt(input, &cwd, &PromptFlag); | |
continue; | |
} | |
check_prompt(&cwd, PromptFlag); | |
if (strcmp(input, "exit") == 0) | |
{ | |
break; | |
} | |
if (check_if_path(input)) | |
{ | |
set_path(input, PATH); | |
continue; | |
} | |
char **args = parse_arg(input, &direction_type, &file_name); | |
if (strcmp(input, "cd") == 0) | |
{ | |
handle_cd(args, &cwd, PromptFlag); | |
continue; | |
} | |
execute(args, PATH, direction_type, file_name); | |
} | |
return 0; | |
} | |
char *get_hostname() | |
{ | |
FILE *fptr; | |
char str[128]; | |
fptr = fopen("/etc/hostname", "r"); | |
fgets(str, 128, fptr); | |
int len = strlen(str); | |
str[len - 1] = '\0'; | |
char *hostname = str; | |
fclose(fptr); | |
return hostname; | |
} | |
void print_prompt(char *cwd, bool PromptFlag) | |
{ | |
if (PromptFlag == true) | |
{ | |
printf("%s%s%s", BLUE, cwd, WHITE); | |
return; | |
} | |
char *user = getenv("USER"); | |
char *hostname = get_hostname(); | |
printf("%s%s@%s%s:%s%s%s$ ", GREEN, user, hostname, WHITE, BLUE, cwd, | |
WHITE); | |
return; | |
} | |
void remove_leading(char input[]) | |
{ | |
int ind = 0; | |
int len = strlen(input); | |
while (input[ind] == ' ' || input[ind] == '\t' || input[ind] == '\n') | |
{ | |
ind++; | |
} | |
int i = 0; | |
if (ind == 0) | |
{ | |
return; | |
} | |
for (; ind < len; ind++) | |
{ | |
input[i] = input[ind]; | |
i++; | |
} | |
input[i] = '\0'; | |
return; | |
} | |
int read_command(char input[], History **head) | |
{ | |
if (fgets(input, MAX, stdin) == NULL) | |
{ | |
add_history(head, input); | |
return 0; | |
} | |
input[strlen(input) - 1] = '\0'; | |
if (strcmp(input, "")) | |
{ | |
add_history(head, input); | |
} | |
remove_leading(input); | |
return 1; | |
} | |
char *concat(char *str1, char *str2) | |
{ | |
int len1 = strlen(str1); | |
int len2 = strlen(str2); | |
char *str = (char *)malloc((len1 + len2 + 1) * sizeof(char)); | |
int i = 0; | |
for (int j = 0; j < len1; j++) | |
{ | |
str[i] = str1[j]; | |
i++; | |
} | |
for (int j = 0; j < len2; j++) | |
{ | |
str[i] = str2[j]; | |
i++; | |
} | |
str[i] = '\0'; | |
return str; | |
} | |
bool file_exists(char *filename) | |
{ | |
struct stat buffer; | |
return (stat(filename, &buffer) == 0); | |
} | |
char *search_for_path(char *file, char *PATH) | |
{ | |
char *arg = strtok(PATH, ":"); | |
int i = 0; | |
while (arg != NULL) | |
{ | |
char *exe = concat(arg, "/"); | |
exe = concat(exe, file); | |
if (file_exists(exe)) | |
{ | |
return exe; | |
} | |
arg = strtok(NULL, ":"); | |
} | |
return file; | |
} | |
bool check_for_redirection(Direction direction_type) | |
{ | |
if (direction_type == REDIRECT_NONE) | |
{ | |
return false; | |
} | |
return true; | |
} | |
char *get_shell() | |
{ | |
char *shell_path = getenv("SHELL"); | |
char *arg = strtok(shell_path, "/"); | |
char *path; | |
while (arg != NULL) | |
{ | |
path = arg; | |
arg = strtok(NULL, "/"); | |
} | |
return path; | |
} | |
bool check_for_pipe(char **args) | |
{ | |
int count = 0; | |
for (int i = 0; i < ARGS_LENGTH; i++) | |
{ | |
if (strcmp(args[i], "|") == 0) | |
{ | |
count++; | |
} | |
} | |
PIPE_LENGTH = count; | |
return count > 0 ? true : false; | |
} | |
void execute_multiple(char ***cmds, char *PATH) | |
{ | |
int fd[2]; | |
pid_t pid; | |
int fdd = 0; | |
int ret; | |
for (int i = 0; i <= PIPE_LENGTH; i++) | |
{ | |
pipe(fd); | |
pid = fork(); | |
if (pid == 0) | |
{ | |
dup2(fdd, 0); | |
if (i != PIPE_LENGTH) | |
{ | |
close(1); | |
dup(fd[1]); | |
} | |
close(fd[0]); | |
char *path = search_for_path(cmds[i][0], PATH); | |
ret = execv(path, cmds[i]); | |
if (ret < 0) | |
{ | |
printf("%s: command not found: %s\n", get_shell(), cmds[i][0]); | |
exit(errno); | |
} | |
exit(1); | |
} | |
else | |
{ | |
wait(0); | |
close(fd[1]); | |
fdd = fd[0]; | |
} | |
} | |
} | |
void handle_pipes(char **args, char *PATH) | |
{ | |
char **cmds[PIPE_LENGTH + 1]; | |
for (int i = 0; i <= PIPE_LENGTH; i++) | |
{ | |
cmds[i] = (char **)malloc(20 * sizeof(char *)); | |
} | |
int i = 0, pipe_num = 0, index_num = 0; | |
for (i = 0; i < ARGS_LENGTH; i++) | |
{ | |
if (strcmp(args[i], "|") == 0) | |
{ | |
cmds[pipe_num][index_num] = NULL; | |
index_num = 0; | |
pipe_num++; | |
continue; | |
} | |
cmds[pipe_num][index_num] = args[i]; | |
index_num++; | |
} | |
cmds[pipe_num][index_num] = NULL; | |
execute_multiple(cmds, PATH); | |
} | |
void execute(char **args, char *PATH, Direction direction_type, char *file_name) | |
{ | |
int pid, ret; | |
if (check_for_pipe(args)) | |
{ | |
handle_pipes(args, PATH); | |
return; | |
} | |
pid = fork(); | |
if (pid == 0) | |
{ | |
char *path = search_for_path(args[0], PATH); | |
args[0] = path; | |
if (check_for_redirection(direction_type)) | |
{ | |
if (direction_type == REDIRECT_OUTPUT) | |
{ | |
int fd = creat(file_name, 0644); | |
close(1); | |
dup(fd); | |
close(fd); | |
} | |
else | |
{ | |
int fd = open(file_name, O_RDONLY); | |
close(0); | |
dup(fd); | |
close(fd); | |
} | |
} | |
ret = execv(path, args); | |
if (ret < 0) | |
{ | |
printf("%s: command not found: %s\n", get_shell(), args[0]); | |
exit(errno); | |
} | |
} | |
else | |
{ | |
wait(0); | |
} | |
} | |
bool change_prompt(char input[]) | |
{ | |
int len = strlen(input); | |
if (len >= 6 && input[0] == 'P' && input[1] == 'S' && input[2] == '1' && | |
input[3] == '=' && input[4] == '"' && input[len - 1] == '"') | |
{ | |
return true; | |
} | |
return false; | |
} | |
bool change_to_pwd(char input[]) | |
{ | |
int len = strlen(input); | |
if (len == 9 && input[5] == '\\' && input[6] == 'w' && input[7] == '$') | |
{ | |
return true; | |
} | |
return false; | |
} | |
char *parse_prompt(char input[]) | |
{ | |
int len = strlen(input) - 5; | |
char *buffer = (char *)malloc(len * sizeof(char)); | |
buffer[len - 1] = '\0'; | |
for (int i = 0; i < len - 1; i++) | |
{ | |
buffer[i] = input[i + 5]; | |
} | |
return buffer; | |
} | |
void handle_prompt(char *input, char **cwd, bool *PromptFlag) | |
{ | |
*PromptFlag = true; | |
if (change_to_pwd(input)) | |
{ | |
*cwd = dir(); | |
*PromptFlag = false; | |
} | |
else | |
{ | |
*cwd = parse_prompt(input); | |
} | |
} | |
void check_prompt(char **cwd, bool PromptFlag) | |
{ | |
if (PromptFlag != true) | |
{ | |
*cwd = dir(); | |
} | |
return; | |
} | |
char **parse_arg(char *input, Direction *direction_type, char **file_name) | |
{ | |
char **args = (char **)malloc(20 * sizeof(char *)); | |
char *arg = strtok(input, " "); | |
int i = 0; | |
while (arg != NULL) | |
{ | |
if (strcmp(arg, ">") == 0) | |
{ | |
ARGS_LENGTH = i; | |
args[i] = NULL; | |
arg = strtok(NULL, " "); | |
*file_name = arg; | |
*direction_type = REDIRECT_OUTPUT; | |
return args; | |
} | |
if (strcmp(arg, "<") == 0) | |
{ | |
ARGS_LENGTH = i; | |
args[i] = NULL; | |
arg = strtok(NULL, " "); | |
*file_name = arg; | |
*direction_type = REDIRECT_INPUT; | |
return args; | |
} | |
args[i] = arg; | |
arg = strtok(NULL, " "); | |
i++; | |
} | |
ARGS_LENGTH = i; | |
args[i] = NULL; | |
*direction_type = REDIRECT_NONE; | |
return args; | |
} | |
bool check_if_path(char input[]) | |
{ | |
int len = strlen(input); | |
if (len >= 6 && input[0] == 'P' && input[1] == 'A' && input[2] == 'T' && | |
input[3] == 'H' && input[4] == '=' && input[5] == '"' && | |
input[len - 1] == '"') | |
{ | |
return true; | |
} | |
return false; | |
} | |
char *parse_path(char *input) | |
{ | |
int len = strlen(input) - 6; | |
char *buffer = (char *)malloc(len * sizeof(char)); | |
buffer[len - 1] = '\0'; | |
for (int i = 0; i < len - 1; i++) | |
{ | |
buffer[i] = input[i + 6]; | |
} | |
return buffer; | |
} | |
void set_path(char *input, char PATH[]) | |
{ | |
char *new_path = parse_path(input); | |
int len = strlen(new_path), i = 0; | |
for (i = 0; i < len && i < MAX; i++) | |
{ | |
PATH[i] = new_path[i]; | |
} | |
PATH[i] = '\0'; | |
} | |
char *find_path_after_home(char *home_path) | |
{ | |
int len = strlen(home_path); | |
char *path = (char *)malloc(len * sizeof(char)); | |
for (int i = 0; i < len; i++) | |
{ | |
path[i] = home_path[i + 1]; | |
} | |
path[len - 1] = '\0'; | |
return path; | |
} | |
char *find_home(char *path) | |
{ | |
char *dir = "/home/"; | |
char *user = getenv("USER"); | |
char *home = concat(dir, user); | |
home = concat(home, find_path_after_home(path)); | |
return home; | |
} | |
void handle_cd(char **args, char **cwd, bool PromptFlag) | |
{ | |
int ret; | |
if (args[1][0] == '~') | |
{ | |
ret = chdir(find_home(args[1])); | |
} | |
else | |
{ | |
ret = chdir(args[1]); | |
} | |
if (ret < 0) | |
{ | |
printf("cd: no such file or directory: %s\n", args[1]); | |
} | |
if (PromptFlag != true) | |
{ | |
*cwd = dir(); | |
} | |
} | |
bool checkIfHome(char *path, char *home) | |
{ | |
int hlen = strlen(home); | |
int plen = strlen(path); | |
if (plen < hlen) | |
{ | |
return false; | |
} | |
for (int i = 0; i < hlen; i++) | |
{ | |
if (home[i] != path[i]) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
char *findDir(char *path, char *home) | |
{ | |
int plen = strlen(path); | |
int hlen = strlen(home); | |
if (plen == hlen) | |
{ | |
return "~"; | |
} | |
char *dir = (char *)malloc((plen - hlen + 2) * sizeof(char)); | |
dir[0] = '~'; | |
int i = 0; | |
for (i = 0; i <= plen - hlen; i++) | |
{ | |
dir[i + 1] = path[hlen + i]; | |
} | |
dir[i] = '\0'; | |
return dir; | |
} | |
char *dir() | |
{ | |
char *dir = "/home/"; | |
char *user = getenv("USER"); | |
char *home = concat(dir, user); | |
char *path = malloc(256 * sizeof(char)); | |
char cwd[1024]; | |
getcwd(cwd, sizeof(cwd)); | |
path = cwd; | |
if (checkIfHome(path, home)) | |
{ | |
return findDir(path, home); | |
} | |
return path; | |
} | |
void add_history(History **head, char *command) | |
{ | |
History *temp = NULL, *ptr = NULL; | |
temp = (History *)malloc(sizeof(History)); | |
temp->command = (char *)malloc(sizeof(char) * (strlen(command) + 1)); | |
strcpy(temp->command, command); | |
temp->next = NULL; | |
if (*head == NULL) | |
{ | |
*head = temp; | |
} | |
else | |
{ | |
ptr = *head; | |
while (ptr->next != NULL) | |
{ | |
ptr = ptr->next; | |
} | |
ptr->next = temp; | |
} | |
} | |
void print_history(History *head) | |
{ | |
History *ptr = head; | |
int i = 1; | |
while (ptr != NULL) | |
{ | |
printf(" %d %s\n", i, ptr->command); | |
ptr = ptr->next; | |
i++; | |
} | |
free(ptr); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment