New Shell Creation
Question:
Construct a new Linux shell. Your shell will be called msh (Merrimack Shell).
Solution:
#include < stdio.h>
#include < unistd.h>
#include < sys times.h="">
#include < time.h>
#include < dirent.h>
#include < stdio.h>
#include < string.h>
#include < unistd.h>
#include < stdlib.h>
#include < ctype.h>
#include < sys wait.h="">
#include < sys types.h="">
#include < sys wait.h="">
#include < fcntl.h>
#include < stdbool.h>
#include < time.h>
#include < sys time.h="">
#include < sys stat.h="">
#define MAX_LINE 80 /* The maximum length command */
#define ANSI_COLOR_RESET "\x1b[0m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_GREEN "\x1b[92m"
#define ANSI_COLOR_BLUE "\x1b[94m"
#define pipenow (alternate ? _file : file)
#define futurepipe (alternate ? file : _file)
#define READ 0
#define WRITE 1
#define CHILD 0
#define ERROR -1
#define PARENT default
#define previouspipe futurepipe
typedef enum {
begining,
center,
ending,
pipebool
} pipestruct;
/*****************************
* We will use 2 pipes and *
* alternate between them *
* for to acheive many pipes *
*****************************/
typedef int pipe_io[2];
char **completecmd, **futurecmd, **cmd;
char input_file[512], output_file[512], error_file[512];
size_t i, semicolons, next, pipes;
bool cmdline, background, alternate;
int infile, outfile, errfile;
pipe_io file, _file;
pid_t pid;
void executeWithPipes(char* cmds);
void fillPipes();
void startWithPipe(pipestruct pipetype);
void redirect();
void redirect_in(size_t index);
void redirect_out(size_t index);
void redirect_err(size_t index);
void start(char** cmd, pipestruct pipetype);
void clear(char** cmd);
void fillPipes()
{
size_t i, j;
pipestruct pipetype = begining;
alternate = false;
for (i = 0; cmd[i] != NULL; i++)
if (!strcmp(cmd[i], "|")) {
clear(&cmd[i]);
while (true) {
// run a pipe than decrease one from the sum
if (pipe(pipenow) != ERROR) {
startWithPipe(pipetype);
pipes--;
if (*cmd == NULL)
return;
for (j = 0; cmd[j] != NULL; j++)
if (!strcmp(cmd[j], "|"))
clear(&cmd[j]);
;
//if it's the last pipe
if (!(pipes > 0)) {
startWithPipe(ending);
return;
}
alternate = alternate ? false : true;
}
else
perror("pipe()");
pipetype = center;
}
};
}
void startWithPipe(pipestruct pipetype)
{
while (*cmd != NULL) {
start(cmd, pipetype);
// free all old commands
for (i = 0; i < next; i++)
if (cmd != NULL)
clear(&cmd[i]);
;
cmd = &cmd[next];
// if there is more pipes
if (pipes > 0)
break;
}
}
int child_pid = 0;
int killall=0;
time_t startinternal;
int timedout = 0;
int timedoutsec = 0;
void start(char** cmd, pipestruct pipetype)
{
void (*getfuturecmd)() = redirect;
/* fork a child process*/
switch (pid = fork()) {
case ERROR:
perror("fork()");
break;
case CHILD:
if (*cmd == NULL)
exit(EXIT_SUCCESS);
switch (pipetype) {
/* if it's the first element in pipes */
case begining:
dup2(pipenow[WRITE], STDOUT_FILENO);
break;
/* if it's the middle element in pipes */
case center:
dup2(previouspipe[READ], STDIN_FILENO);
dup2(pipenow[WRITE], STDOUT_FILENO);
break;
/* if it's the last element in pipes */
case ending:
dup2(pipenow[READ], STDIN_FILENO);
default:
break;
}
redirect();
execvp(*cmd, cmd);
perror(*cmd);
_exit(EXIT_FAILURE);
/* if it's the parent process decide wether to wait based on & */
PARENT:
child_pid = pid;
switch (pipetype) {
case begining:
close(pipenow[WRITE]);
break;
case center:
close(previouspipe[READ]);
close(pipenow[WRITE]);
break;
case ending:
close(pipenow[READ]);
default:
break;
}
if (!background) {
if (timedout == 1) {
alarm(timedoutsec);
int state;
waitpid(pid, &state, 0);
printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state));
}
else
while (wait(NULL) > 0)
;
}
getfuturecmd();
}
}
void redirect()
{
size_t i;
for (i = 0; cmd[i] != NULL; i++) {
if (!strcmp(cmd[i], "<"))
redirect_in(i++);
else if (!strcmp(cmd[i], ">"))
redirect_out(i++);
else if (!strcmp(cmd[i], "2>"))
redirect_err(i++);
}
if (pipes > 0)
i++;
next = i;
}
void redirect_in(size_t i)
{
if (pid == CHILD) {
clear(&cmd[i]);
strcpy(input_file, cmd[++i]);
clear(&cmd[i]);
infile = open(input_file, O_RDONLY);
if (infile != ERROR) {
dup2(infile, STDIN_FILENO);
close(infile);
}
else {
perror(input_file);
exit(EXIT_SUCCESS);
}
}
}
void redirect_out(size_t i)
{
if (pid == CHILD) {
clear(&cmd[i]);
strcpy(output_file, cmd[++i]);
clear(&cmd[i]);
outfile = open(output_file, O_RDWR | O_CREAT, 0777);
if (outfile != ERROR) {
dup2(outfile, STDOUT_FILENO);
close(outfile);
}
else {
perror(output_file);
exit(EXIT_SUCCESS);
}
}
}
void redirect_err(size_t i)
{
if (pid == CHILD) {
clear(&cmd[i]);
strcpy(error_file, cmd[++i]);
clear(&cmd[i]);
errfile = open(error_file, O_RDWR | O_CREAT, 0777);
if (errfile != ERROR) {
dup2(errfile, STDERR_FILENO);
close(errfile);
}
else {
perror(error_file);
exit(EXIT_SUCCESS);
}
}
}
void executeWithPipes(char* cmds)
{
size_t max = 1024;
char* token;
i = 0;
semicolons = 0;
completecmd = malloc(sizeof(char*) * max);
token = strtok(cmds, " ");
while (token != NULL) {
if (!strcmp(token, ";")) {
completecmd[i] = NULL;
semicolons++;
}
else {
completecmd[i] = malloc(sizeof(char) * (strlen(token) + 1));
strcpy(completecmd[i], token);
}
token = strtok(NULL, " ");
if (i == max)
completecmd = realloc(completecmd, max *= 2);
i++;
}
completecmd[i] = NULL;
if (!strcmp(completecmd[--i], "&")) {
background = true;
clear(&completecmd[i]);
}
else
background = false;
cmd = completecmd;
size_t i;
while (true) {
pipes = 0;
for (i = 0; cmd[i] != NULL; i++)
if (!strcmp(cmd[i], "|"))
pipes++;
;
if (pipes > 0)
fillPipes();
else
startWithPipe(pipebool);
if (semicolons > 0) {
cmd = &cmd[1];
semicolons--;
}
else
break;
}
free(completecmd);
}
void clear(char** cmd)
{
free(*cmd);
*cmd = NULL;
}
char** splitstring(char* strs, int* n_spaces)
{
char** res = NULL;
*n_spaces = 0;
char* str = (char*)malloc(strlen(strs) + 1);
strcpy(str, strs);
char* p = strtok(str, " ");
/* split string and append tokens to 'res' */
while (p) {
*n_spaces = *n_spaces + 1;
res = (char**)realloc(res, sizeof(char*) * (*n_spaces));
if (res == NULL)
exit(-1); /* memory allocation failed */
res[*n_spaces - 1] = p;
p = strtok(NULL, " ");
}
/* realloc one extra element for the last NULL */
res = (char**)realloc(res, sizeof(char*) * (*n_spaces + 1));
res[*n_spaces] = 0;
return res;
}
void process_terminated(pid_t child_pid)
{
printf("\n\nterminated\n\n");
}
static char* util_cat(char* dest, char* end, const char* str)
{
while (dest < end && *str)
*dest++ = *str++;
return dest;
}
size_t join_str(char* out_string, size_t out_bufsz, const char* delim, char** chararr)
{
char* ptr = out_string;
char* strend = out_string + out_bufsz;
while (ptr < strend && *chararr) {
ptr = util_cat(ptr, strend, *chararr);
chararr++;
if (*chararr)
ptr = util_cat(ptr, strend, delim);
}
return ptr - out_string;
}
time_t timepast24h;
time_t timenow;
time_t startime;
time_t endtime;
void printDate(time_t t)
{
char date[16];
strftime(date, 16, "%d-%m-%y %H:%M", localtime(&t));
printf("Date : %s\n", date);
}
void printDateraw(time_t t)
{
char date[36];
strftime(date, 26, "%d-%m-%y " ANSI_COLOR_GREEN " %H:%M " ANSI_COLOR_RESET, localtime(&t));
printf("%s", date);
}
void printDateraw1(time_t t)
{
char date[36];
strftime(date, 26, "%d-%m-%y %H:%M ", localtime(&t));
printf("%s", date);
}
void printHour(time_t t)
{
char date[16];
strftime(date, 16, "%H:%M", localtime(&t));
printf("Date : %s", date);
}
int n = 0;
void mtime_cmd(char* basePath, const int root, time_t starttime1, time_t endtime1, int iprint)
{
if (timedout==1 && time(NULL) > startinternal + timedout) return;
int i;
char path[1000];
struct dirent* dp;
DIR* dir = opendir(basePath);
if (!dir)
return;
while ((dp = readdir(dir)) != NULL) {
if (timedout==1 && time(NULL) > startinternal + timedout) return;
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
struct stat attr;
char path1[1000];
strcpy(path1, basePath);
strcat(path1, "/");
strcat(path1, dp->d_name);
stat(path1, &attr);
struct tm* ts = localtime(&attr.st_mtime);
if (attr.st_mtime > starttime1 && attr.st_mtime <= endtime1) {
printf(ANSI_COLOR_BLUE);
if (iprint == 1)
printf(" ");
if (iprint == 1)
printDateraw1(mktime(ts));
if (iprint == 1)
printf(" --> %s/%s", basePath, dp->d_name);
if (iprint == 1)
printf("\n");
printf(ANSI_COLOR_RESET);
for (i = 0; i < root; i++) {
/*if (i%2 == 0 || i == 0)
printf("│");
else
printf(" ");*/
}
//printf("├─%s\n", dp->d_name);
n++;
}
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp->d_name);
mtime_cmd(path, root + 2, starttime1, endtime1, iprint);
}
}
closedir(dir);
}
int main()
{
int i, j;
int sizeCmd = 0;
int measure = 0;
char cmd[MAX_LINE / 2 + 1]; /* command line arguments */
int should_run = 1, towait = 1; /* flag to determine when to exit program */
char s[256];
int internalcmd = 0;
/*******************/
char** history = NULL;
int sz_history = 0;
/*******************/
while (should_run) {
internalcmd = 0;
timedout = 0;
measure = 0;
printf(ANSI_COLOR_GREEN);
printf("\nmsh> ");
printf(ANSI_COLOR_RESET);
int ret = scanf(" %[^\n]s", cmd);
//CTRL+D
if (ret == EOF)
return 0;
if (strcmp(cmd, "!!") == 0) {
if (sz_history > 0) {
strcpy(cmd, history[sz_history - 1]);
//history[ sz_history-1] = NULL;
printf(ANSI_COLOR_BLUE);
printf("\n%s\n", cmd);
printf(ANSI_COLOR_RESET);
}
else {
printf("\nNo commands in history.\n");
internalcmd = 1;
}
}else if(cmd[0]=='!') {
cmd[0]='0';
int n=atoi(cmd);
if (sz_history >= n) {
strcpy(cmd, history[sz_history - n]);
//history[ sz_history-1] = NULL;
printf(ANSI_COLOR_BLUE);
printf("\n%s\n", cmd);
printf(ANSI_COLOR_RESET);
}
else {
printf("\nNo commands in history.\n");
internalcmd = 1;
}
}
else {
sz_history = sz_history + 1;
history = (char**)realloc(history, sizeof(char*) * (sz_history + 1));
char* str = (char*)malloc(strlen(cmd) + 1);
strcpy(str, cmd);
history[sz_history - 1] = str;
history[sz_history] = 0;
}
redo: killall=0;
internalcmd = 0;
char** res = splitstring(cmd, &sizeCmd);
if (strcmp(res[0], "exit") == 0 || strcmp(res[0], "quit") == 0) {
should_run = 0;
internalcmd = 1;
}
printf(ANSI_COLOR_BLUE);
if (strcmp(res[0], "pwd") == 0) {
printf("%s\n", getcwd(s, 100));
internalcmd = 1;
}
printf(ANSI_COLOR_RESET);
if (strcmp(res[0], "cd") == 0) {
if (sizeCmd > 1 && strlen(res[1]) > 0)
chdir(res[1]);
internalcmd = 1;
}
towait = 1;
if (sizeCmd > 0 && strlen(res[sizeCmd - 1]) > 0) {
char c = res[sizeCmd - 1][strlen(res[sizeCmd - 1]) - 1];
if (c == '&') {
towait = 0;
res[sizeCmd - 1][strlen(res[sizeCmd - 1]) - 1] = 0;
}
}
if (strlen(res[sizeCmd - 1]) == 0)
res[sizeCmd - 1] = 0;
printf(ANSI_COLOR_RESET);
if (internalcmd == 0) {
struct tms times_start, times_end;
clock_t times_start_retval, times_end_retval;
if ((times_start_retval = times(×_start)) == -1) {
perror("starting times");
return -1;
}
struct timeval start, end;
gettimeofday(&start, NULL);
clock_t t;
t = clock();
/************************/
if (timedout == 1) {
char path1[1000] = "";
for (j = 2; j < sizeCmd; j++) {
strcat(path1, res[j]);
strcat(path1, " ");
}
executeWithPipes(path1);
}
else if (measure == 1) {
char path1[1000] = "";
for (j = 1; j < sizeCmd; j++) {
strcat(path1, res[j]);
strcat(path1, " ");
}
executeWithPipes(path1);
}
else
executeWithPipes(cmd);
/************************/
t = clock() - t;
gettimeofday(&end, NULL);
double time_taken = (1000 * (double)t) / CLOCKS_PER_SEC;
long seconds = (end.tv_sec - start.tv_sec);
long micros = ((seconds * 1000000) + end.tv_usec) - (start.tv_usec);
if ((times_end_retval = times(×_end)) == -1) {
perror("ending timer");
return -1;
}
if (measure == 1) {
printf("\nCPU Time elpased is %f milliseconds \n", time_taken);
printf("Wall Time elpased is %f milliseconds ", (double)micros / 1000);
printf("\nSystem Time elpased is %f milliseconds ", (double)times_end.tms_cstime - times_start.tms_cstime / CLOCKS_PER_SEC);
}
}
free(res);
fflush(stdout);
}
return 0;
}