Skip to content

Instantly share code, notes, and snippets.

@TanmayPatil105
Last active March 12, 2024 08:44
Show Gist options
  • Save TanmayPatil105/010550ec9bac2a91b8705deee95337b1 to your computer and use it in GitHub Desktop.
Save TanmayPatil105/010550ec9bac2a91b8705deee95337b1 to your computer and use it in GitHub Desktop.
#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