hsuay
4/3/2019 - 4:49 PM

UNIX shell written in C.

UNIX shell written in C.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <math.h>
#include <string.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>

#define rep(i, a, b) for(i = a; i < b; i++)
#define rev(i, a, b) for(i = a; i > b; i--)
#define TOK_BUFFER 64
#define COMM_BUFFER 64
#define TOK_DELIM " \t\n\a\r"
#define COMM_DELIM ";"
char* root;
int shell_cd (char **args);
int shell_exit (char **args);
int shell_quit(char **args);
int shell_pwd (char **args);
int shell_echo (char **args);
int shell_pinfo (char **args);
int shell_ls (char **args);
int shell_setenv (char **args);
int shell_unsetenv (char **args);

char *builtin_arr[] = {"cd","exit","pwd","echo","pinfo", "ls","setenv","unsetenv","quit"};
int (*builtin_functions[])(char**) = {&shell_cd,&shell_exit,&shell_pwd,&shell_echo,&shell_pinfo,&shell_ls,&shell_setenv,&shell_unsetenv,&shell_quit};


void sigintHandler(int sig_num) {
	signal(SIGINT,sigintHandler);
	fflush(stdout);
}


void pipehandler(char **args){
	// File descriptors
	int filedes[2];
	int filedes2[2];
	
	int num_cmds = 0;
	
	char *command[256];
	
	pid_t pid;
	
	int err = -1,end = 0, i = 0, j = 0,k = 0,l = 0;
	
	while (args[l] != NULL){
		if (strcmp(args[l],"|") == 0){
			num_cmds++;
		}
		l++;
	}
	num_cmds++;
	
	while (args[j] != NULL && end != 1){
		k = 0;
	
		while (strcmp(args[j],"|") != 0){
			command[k] = args[j];
			j++;	
			if (args[j] == NULL){
				end = 1;
				k++;
				break;
			}
			k++;
		}
		command[k] = NULL;
		j++;		
		if (i % 2 != 0){
			pipe(filedes); 
		}else{
			pipe(filedes2); 
		}
		
		pid=fork();
		
		if(pid==-1){			
			if (i != num_cmds - 1){
				if (i % 2 != 0){
					close(filedes[1]); // for odd i
				}else{
					close(filedes2[1]); // for even i
				} 
			}			
			printf("Child process could not be created\n");
			return;
		}
		if(pid==0){

			if (i == 0){
				dup2(filedes2[1], STDOUT_FILENO);
			}

			else if (i == num_cmds - 1){
				if (num_cmds % 2 != 0){
					dup2(filedes[0],STDIN_FILENO);
				}else{ 
					dup2(filedes2[0],STDIN_FILENO);
				}
			}else{ 
				if (i % 2 != 0){
					dup2(filedes2[0],STDIN_FILENO); 
					dup2(filedes[1],STDOUT_FILENO);
				}else{ 
					dup2(filedes[0],STDIN_FILENO); 
					dup2(filedes2[1],STDOUT_FILENO);					
				} 
			}
			
			if (execvp(command[0],command)==err){
				kill(getpid(),SIGTERM);
			}		
		}
				
		if (i == 0){
			close(filedes2[1]);
		}
		else if (i == num_cmds - 1){
			if (num_cmds % 2 != 0){					
				close(filedes[0]);
			}else{					
				close(filedes2[0]);
			}
		}else{
			if (i % 2 != 0){					
				close(filedes2[0]);
				close(filedes[1]);
			}else{					
				close(filedes[0]);
				close(filedes2[1]);
			}
		}
				
		waitpid(pid,NULL,0);
				
		i++;	
	}
}


int redirect(char **args, int num)
{
	char in[100],out[100],app[100];
	int r = 0,pid,flag1=0,flag2=0,flag3=0;
	args[num] = NULL;

	pid = fork();
	if (pid==0)
	{	
		for (int i=0;i<num;i++)
		{
			if (strcmp(args[i],"<")==0)
			{
				args[i] = NULL;
				strcpy(in,args[i+1]);
				flag1 = 1;
			}
			
			if (args[i]!=NULL)
			{
				if (strcmp(args[i],">")==0)
				{
					args[i] = NULL;
					strcpy(out,args[i+1]);
					flag2 = 1;
				}
			}

			if (args[i]!=NULL)
			{
				if (args[i][1]==62)
				{
					args[i] = NULL;
					strcpy(app,args[i+1]);
					flag3 = 1;
				}
			}
		}
		if (flag1)
		{
			int fd;
			fd = open(in,O_RDONLY,0);
			if (fd<0)
			{perror("Could not open input file"); exit(0);}

			dup2(fd,0);
			close(fd);
		}	

		if (flag2)
		{
			struct stat buf;
			if (stat(out,&buf)==0)
			{
				int fo;	
				fo = open(out, O_WRONLY);
				if (fo<0)
				{perror("Could not open output file"); exit(0);}
				dup2(fo,1);
				close(fo);
			}
			else
			{
				int fo;
				fo = creat(out,0644);
				if (fo<0)
				{perror("Could not create output file"); exit(0);}
				dup2(fo,1);
				close(fo);
			}
		}

		if (flag3)
		{
			struct stat buffer;
			if (stat(app,&buffer)==0)
			{
				int fa;
				fa = open(app,O_APPEND | O_WRONLY);
				if (fa<0)
				{perror("Could not open output file"); exit(0);}
				dup2(fa,1);
				close(fa);
			}
			else
			{
				int fa;
				fa = creat(app,0644);
				if (fa<0)
				{perror("Could not create output file"); exit(0);}
				dup2(fa,1);
				close(fa);
			}
		}		
		if (execvp(args[0],args)<0)	
		{r=1; printf("%s: Command doesn't exist\n", args[0]);}
	}
	else
	{wait(NULL);}
	if (r!=1)
	{printf("%s with process id: %d exited normally\n",args[0],pid);}					
	return 1;
}





int shell_setenv (char **args) {
	if (args[2] == NULL) args[2] = " ";
	if (setenv(args[1],args[2],1) != 0) perror("shell");
	return 1;	
}

int shell_unsetenv (char **args) {
	if (unsetenv(args[1]) != 0) perror("shell");
	return 1;
}


int shell_cd (char** args) {
    if (args[1] == NULL)
        return 1;
    else if (strcmp(args[1],"~")==0){
        chdir(root);
    }
    else if (chdir(args[1]) != 0)
        perror("SHELL");
    return 1;
}

int shell_exit (char** args) {
    return EXIT_SUCCESS;
}

int shell_quit (char** args) {
    return EXIT_SUCCESS;
}

int shell_pwd (char** args) {
    char *curr_dir = (char *)malloc(1000*sizeof(char)); 
    getcwd(curr_dir, 1000); 
    printf("%s\n", curr_dir);
    free(curr_dir);
    return 1;
}

int shell_echo (char** args) {
    int i = 1;
    while(args[i] != NULL) {
        printf("%s ", args[i]);
        i++;
    }
    printf("\n");  
    return 1;
}

int shell_pinfo (char ** args) {
    char Process[1000];
    strcpy(Process, "/proc/");
    if(args[1]) 
        strcat(Process, args[1]);
    else 
        strcat(Process, "self");
    char Stats[100];
    strcpy(Stats, Process); strcat(Stats, "/stat");
    int error_number[3];
    error_number[0] = 0;
    FILE * stat = fopen(Stats, "r");
    if(error_number[0]) {
        fprintf(stderr, "Error reading %s: %s\n", Stats, strerror(error_number[0]));
        return 1;
    }

    int pid; char status; char name[20];
    fscanf(stat, "%d", &pid); fscanf(stat, "%s", name); fscanf(stat, " %c", &status);
    printf( "pid: %d\n", pid);
    printf( "Process Status: %c\n", status);
    fclose(stat);
    
    error_number[0] = 0;
    strcpy(Stats, Process); strcat(Stats, "/statm");
    FILE * mem = fopen(Stats, "r");
    
    if(error_number[0])     {
	fprintf(stderr, "Error reading %s: %s\n", Stats, strerror(error_number[0]));
        return 1;
    }
    int memSize; fscanf(mem, "%d", &memSize);
    fprintf(stdout, "Memory: %d\n", memSize);
    fclose(mem);
    char exePath[1000];
    strcpy(Stats, Process); strcat(Stats, "/exe");
    error_number[0] = 0;

    readlink(Stats, exePath, sizeof(exePath));
    if(error_number[0]) {
        fprintf(stderr, "Error reading symbolic link %s: %s\n", Stats, strerror(error_number[0]));
        return 1;
    }

    int sameChars = 0, baseL = strlen(root);
    for(sameChars = 0; sameChars < baseL; sameChars++)
        if(root[sameChars] != exePath[sameChars]) break;;
    
    char relPath[1000];
    if(sameChars == baseL) {
        relPath[0] = '~'; relPath[1] = '\0';
        strcat(relPath, (const char *)&exePath[baseL]);
    }
    else {
        strcpy(relPath, exePath);
        relPath[strlen(exePath)] = '\0';
    }

    int i = 0;
    while(exePath[i]) exePath[i++] = '\0';
    
    fprintf(stdout, "Executable Path: %s\n", relPath);
    return 1;
}

int shell_ls (char** args) {
	DIR* dir;
    struct stat mystat[4];
    struct passwd *user[2];
	struct group *group[2];
    char *buffer;
    int count[6];
    count[1] = 0;
    count[2] = 0;
    count[3] = 0;
    count[4] = 1;
	count[5] = 0;
	unsigned char mod[13];
	struct tm *starttime[2];
	time_t now;
	int year; 
    struct dirent **dient[4];
    buffer = malloc(1024*sizeof(char));
	while(args[count[4]] != NULL) {
		count[5] = 0;
		if (args[count[4]][count[5]] == 45) {
			count[5] = 1;
			while(args[count[4]][count[5]] != 0) {
				if(args[count[4]][count[5]] == 108) count[1] = 1;
				if(args[count[4]][count[5]] == 97)  count[2] = 1;
				count[5]++;
			}
		}
		else {
			if (count[4] > 0) count[3] = count[4];
		}
		count[4]++; 
	}
	if (count[3] == 0 || args[count[3]] == NULL) args[count[3]] = ".";
		count[0] = scandir(".",&dient[0],NULL,alphasort);		
	count[0] = scandir(args[count[3]],&dient[0],NULL,alphasort);
	if (count[0] < 0) perror("shell");
	else {
		for(count[4]=0;count[4]<count[0];count[4]++) {
			if (count[1] == 1) {
				if (count[2] == 1) {
					sprintf(buffer,"%s/%s",args[count[3]],dient[0][count[4]]->d_name);
					stat(buffer,&mystat[0]);
					printf( (S_ISDIR(mystat[0].st_mode)) ? "d" : "-");
					printf( (mystat[0].st_mode & S_IRUSR) ? "r" : "-");
					printf( (mystat[0].st_mode & S_IWUSR) ? "w" : "-");
					printf( (mystat[0].st_mode & S_IXUSR) ? "x" : "-");
					printf( (mystat[0].st_mode & S_IRGRP) ? "r" : "-");
					printf( (mystat[0].st_mode & S_IWGRP) ? "w" : "-");
					printf( (mystat[0].st_mode & S_IXGRP) ? "x" : "-");
					printf( (mystat[0].st_mode & S_IROTH) ? "r" : "-");
					printf( (mystat[0].st_mode & S_IWOTH) ? "w" : "-");
					printf( (mystat[0].st_mode & S_IXOTH) ? "x" : "-");
					printf(" \t%d",(int)mystat[0].st_nlink);
					user[1] = getpwuid(mystat[0].st_uid);
						printf(" \t%s", user[1]->pw_name);
					group[1] = getgrgid(mystat[0].st_gid);
						printf(" \t%s", group[1]->gr_name);
					printf(" \t%lld",(long long)mystat[0].st_size);
					time(&now);
					year = localtime(&now)->tm_year;
					starttime[1] = localtime(&mystat[0].st_ctime);
					if(starttime[1]->tm_year == year)
						strftime(mod,13,"%b %e %R",starttime[1]);
					else
						strftime(mod,13,"%b %e %Y",starttime[1]);
					printf(" \t%s",mod );	 	
					printf(" \t%s\n",dient[0][count[4]]->d_name);
				}
				else {
					if ((dient[0][count[4]] ->d_name)[0] != 46) {
						sprintf(buffer,"%s/%s",args[count[3]],dient[0][count[4]]->d_name);
						stat(buffer,&mystat[0]);
						printf( (S_ISDIR(mystat[0].st_mode)) ? "d" : "-");
						printf( (mystat[0].st_mode & S_IRUSR) ? "r" : "-");
						printf( (mystat[0].st_mode & S_IWUSR) ? "w" : "-");
						printf( (mystat[0].st_mode & S_IXUSR) ? "x" : "-");
						printf( (mystat[0].st_mode & S_IRGRP) ? "r" : "-");
						printf( (mystat[0].st_mode & S_IWGRP) ? "w" : "-");
						printf( (mystat[0].st_mode & S_IXGRP) ? "x" : "-");
						printf( (mystat[0].st_mode & S_IROTH) ? "r" : "-");
						printf( (mystat[0].st_mode & S_IWOTH) ? "w" : "-");
						printf( (mystat[0].st_mode & S_IXOTH) ? "x" : "-");
						printf(" \t%d",(int)mystat[0].st_nlink);
						user[1] = getpwuid(mystat[0].st_uid);
						printf(" \t%s", user[1]->pw_name);
						group[1]=getgrgid(mystat[0].st_gid);
						printf(" \t%s", group[1]->gr_name);
						printf(" \t%lld",(long long)mystat[0].st_size);
						time(&now);
						year = localtime(&now)->tm_year;
						starttime[1] = localtime(&mystat[0].st_ctime);
						if(starttime[1]->tm_year == year)
							strftime(mod,13,"%b %e %R",starttime[1]);
						else
							strftime(mod,13,"%b %e %Y",starttime[1]);
						printf(" \t%s",mod );
						printf(" \t%ld",mystat[0].st_mtime);
						printf(" \t%s\n",dient[0][count[4]]->d_name);
					}	 
				}
			}	
			else {
				if (count[2] == 1) {
					printf(" %s\n",dient[0][count[4]]->d_name);	
				}
				else {
					if ((dient[0][count[4]] ->d_name)[0] != 46) printf("%s   ",dient[0][count[4]]->d_name);
				}
			}	
			free(dient[0][count[4]]);
			
		}
		free(dient[0]);
	}	
	return 1; 

}

char* returnPath (char* cwd) {
    int i,cwd_size = strlen(cwd), root_size = strlen(root);
    if (root_size > cwd_size) {
        return cwd;
    }
    else if (root_size == cwd_size) {
        return "~";
    }
    else {
        char *new = (char*)malloc(100);
        new[0] = '~';
        new[1] = '/';
        for (i = 0 ; i < cwd_size-root_size-1; i++) {
            new[i+2] = cwd[root_size+i+1];
        }
        return new;
    }
} 

void printPrompt (char *root) {
    char hostname[1024];
    char cwd[1024];
    hostname[1023] = '\0';
    gethostname(hostname, 1023);
    char *username = getenv("USER");
    getcwd(cwd, sizeof(cwd));
    char *path = returnPath(cwd);
    printf("\n<%s@%s:%s/> ",username, hostname, path);
}

char *readCommands (void) {

    int buffer_size = 1024,check, pos = 0, i;
    char *buffer = malloc(sizeof(char) * buffer_size);
  
    if (!buffer) {
      fprintf(stderr, "ALLOCATION ERROR\n");
      exit(EXIT_FAILURE);
    }
  
    for (i = 0; ;i++) {
      check = getchar();
      if (check == EOF || check == '\n') {
        buffer[pos] = '\0';
        return buffer;
      } 
      else
        buffer[pos] = check;
      
    pos++;
  
      if (pos >= buffer_size) {
        buffer_size += 1024;
        buffer = realloc(buffer, buffer_size);
        if (!buffer) {
          fprintf(stderr, "ALLOCATION ERROR\n");
          exit(EXIT_FAILURE);
        }
      }
    }
}

char **splitLine (char* line) {
    
    int buffer_size = COMM_BUFFER;
    int position  = 0, i = 0;
    char **commands = malloc(buffer_size * (sizeof(char*)));
    char *command;

    if (!commands) {
        fprintf(stderr, "Allocaiton Error \n");
        exit(EXIT_FAILURE);
    }

    command  = strtok(line, COMM_DELIM);
    for (i = 0;; i++) {
        if (command!= NULL) {
        commands[position++] = command;
            if (buffer_size < position) {
                buffer_size += COMM_BUFFER;
                commands = realloc(commands, buffer_size * sizeof(char*));
                if (!commands) {
                    fprintf(stderr, "Allocaiton Error \n");
                    exit(EXIT_FAILURE);
                }
            }   
            command = strtok(NULL, COMM_DELIM);
            continue;
        }
        break;
    }
    commands[position] = NULL;
    return commands;
}


char **splitCommand (char* line) {
    
    int buffer_size = TOK_BUFFER;
    int position  = 0, i = 0;
    char **tokens = malloc(buffer_size * (sizeof(char*)));
    char *token;

    if (!tokens) {
        fprintf(stderr, "Allocaiton Error \n");
        exit(EXIT_FAILURE);
    }

    token  = strtok(line, TOK_DELIM);
    for (i = 0;; i++) {
        if (token!= NULL) {
        tokens[position++] = token;
            if (buffer_size < position) {
                buffer_size += TOK_BUFFER;
                tokens = realloc(tokens, buffer_size * sizeof(char*));
                if (!tokens) {
                    fprintf(stderr, "Allocaiton Error \n");
                    exit(EXIT_FAILURE);
                }
            }   
            token = strtok(NULL, TOK_DELIM);
            continue;
        }
        break;
    }
    tokens[position] = NULL;
    return tokens;
}

int launch (char** args) {
    
    int i,j,background = 0, redirectflag = 0, piping = 0;
    for (i = 0;args[i] != NULL;i++) {
        for (j = 0;args[i][j] != '\0'; j++) {
            if (args[i][j] == '>' || args[i][j] == '<') {
                redirectflag = 1;
            }
            if (args[i][j] == '|') {
                piping = 1;
            }
            if (args[i][j] == '&') {
                background = 1;
            }
        }
    }
    if (piping) {
        pipehandler(args);
        return 1;
    }
    if (redirectflag) {
        return redirect(args, i);   
    }
    pid_t pid, wpid;
    int state;
    pid = fork();
    
    if (pid < 0) {
        perror("ERROR");
    }

    else if(!pid) {
        if ( execvp(args[0], args) == -1)
            perror("ERROR");
        exit(EXIT_FAILURE);
    }
    if(!background) {
        wpid = waitpid (pid, &state, WUNTRACED);
        for (i = 0;; i++) {
        if (!WIFEXITED(state) && !WIFSIGNALED(state)) {
            wpid = waitpid (pid, &state, WUNTRACED);
            continue;
            }
        break;
        }
    }
    return 1;
}

int checkCommand (char** args) {
    int count;
    if (args[0] == NULL)
        return EXIT_FAILURE;

    count = 0;
    while (count < sizeof(builtin_arr)/sizeof(char*)) {
        if (strcmp(args[0],builtin_arr[count]) == 0)
            return (*builtin_functions[count])(args);
        
        count++;
    }
    return launch(args);
}

void interpretCommand(void) {
    char **args;
    char *commands;
    char **split_line;
    int state;
    int i,j;
    signal(SIGINT,sigintHandler);
    for (i = 0;; i++) {
        printPrompt(root);
        commands  = readCommands();
        split_line = splitLine(commands);
        for (j = 0;split_line[j] != NULL ;j++) {
            args = splitCommand(split_line[j]);
            state = checkCommand(args);
            free (args);
            if (!state) {
                break;
            }
        }
        free(commands);
        free(split_line);
        if(!state) {
            break;
        }
    }
}

int main(int arg1, char **arg2) {
    root = getenv("PWD");
    interpretCommand();
    return EXIT_SUCCESS;
}