Making my own shell I wrote this code;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LEN 512
#define MAXARGS 10
#define ARGLEN 30
#define PROMPT "\033[1;34mcs525shell@\033[0m"
int pid;
int execute(char* arglist[]);
char** tokenize(char* cmdline);
char* read_cmd(char*, FILE*);
void sigint_handler(int signum){
kill(pid,9);
signal(SIGINT,SIG_DFL);
system("stty echo");
}
int main(){
char *cmdline;
char** arglist;
char* prompt = PROMPT;
signal(SIGINT,sigint_handler); //SEE HERE I AM CATCHING CTRL + C WHICH IS QUITING LESS PROCESS LIKE "LESS /ETC/PASSWD"
//BUT NOT QUITING MY SHELL. NOW I WANT TO CATCH CTRL + D TO QUIT MY SHELL PROCESS
//
while((cmdline = read_cmd(prompt,stdin)) != NULL){
if((arglist = tokenize(cmdline)) != NULL){
execute(arglist);
// need to free arglist
for(int j=0; j < MAXARGS+1; j++)
free(arglist[j]);
free(arglist);
free(cmdline);
}
}//end of while loop
printf("\n");
return 0;
}
int execute(char* arglist[]){
int status;
pid = fork();
switch(pid){
case -1:
perror("fork failed");
exit(1);
case 0:
execvp(arglist[0], arglist);
perror("Command not found...");
exit(1);
default:
waitpid(pid, &status, 0);
// printf("child exited with status %d \n", status >> 8);
return 0;
}
}
char** tokenize(char* cmdline){
if(cmdline[0] == '\0')//if user has entered nothing and pressed enter key. Writing it before the for loop of allocating
return NULL; //memory is better in a way that it returns NULL and no need to allocate memory.
//allocate memory
char** arglist = (char**)malloc(sizeof(char*)* (MAXARGS+1));
for(int j=0; j < MAXARGS+1; j++){
arglist[j] = (char*)malloc(sizeof(char)* ARGLEN);
bzero(arglist[j],ARGLEN);
}
int argnum = 0; //slots used
char*cp = cmdline; // pos in string
char*start;
int len;
while(*cp != '\0'){
while(*cp == ' ' || *cp == '\t') //skip leading spaces
cp++;
start = cp; //start of the word
len = 1;
//find the end of the word
while(*++cp != '\0' && !(*cp ==' ' || *cp == '\t'))
len++;
strncpy(arglist[argnum], start, len);
arglist[argnum][len] = '\0';
argnum++;
}
arglist[argnum] = NULL;
return arglist;
}
char* read_cmd(char* prompt, FILE* fp){
printf("%s", prompt);
int c; //input character
int pos = 0; //position of character in cmdline
char* cmdline = (char*) malloc(sizeof(char)*MAX_LEN);
while((c = getc(fp)) != EOF){
if(c == '\n')
break;
cmdline[pos++] = c;
}
//these two lines are added, in case user press ctrl+d to exit the shell
if(c == EOF && pos == 0)
return NULL;
cmdline[pos] = '\0';
return cmdline;
}
Version01:
[10]
The first version of cs525shell has been discussed in the class and developed in the video lecture available online at
(
http://www.arifbutt.me/lec22-design-...if-butt-pucit/). It has the
following capabilities/characteristics:
a. The shell program displays a prompt, which is cs525@pwd, i.e., the present working directory. You may like to indicate
other things like machine name or username or any other information you like. Hint: getcwd()
b. The cs525shell allow the user to type a string (command with options and arguments) (if any) in a single line. Parse the
line in tokens, fork and then pass those tokens to some exec family of function(s) for execution. The parent process waits
for the child process to terminate. Once the child process terminates the parent process, i.e., our cs525shell program again
displays the prompt and waits for the user to enter next command.
c. The shell program allows the user to quit the shell program by pressing <CTRL+D>
d. If the user run the less /etc/passwd program and press <CTRL+C>, i.e., send SIGINT to the less program. Both
the less program and the shell should not terminate. Rather the signal should be delivered to the less program only and
not to cs525shell
$ ./cs525shellv1
cs525shell@/home/arif/:- ls –l /etc/passwd
-rw-r—r-- 1 root root 2102 Feb 7 07:35 /etc/passwd
cs525shell@/home/arif/:-