Implementing a shell. External commands, builtins, I/O redirection, and pipes.
#include <stdio.h>
#include <string.h>
#include <stdlib.h> //realloc()
#include <unistd.h> //fork(), pipe()
#include <limits.h> //PATH_MAX
//#include <sys/stat.h>
//#include <sys/types.h>
#include <fcntl.h> //access mode constants, and flags for open()
#include <errno.h> //errno
#include <readline/readline.h>
#include <readline/history.h>
#include <signal.h>
#define HISTFILE "./.history"
int noa;
int TO_BACKGROUND;
/*
* === FUNCTION ======================================================================
* Name: parsecommand
* Description: Take the command string and parse it to create the arguments required
* to invoke execvp
* =====================================================================================
*/
char** parsecommand ( char *command ) {
char *buffer;
char **args;
int last = 0;
char *tokens = " ";
//start tokenising
buffer = strtok( command, tokens);
//obtain memory to store a pointer to the word and store it to args
size_t size = sizeof( buffer );
args = malloc( size );
args[last++] = buffer;
//do {} while, makes sure that a null pointer is added to the args
do{
buffer = strtok( NULL, tokens) ;
size += size;
args = realloc( args, size );
args[last++] = buffer;
}while( buffer != NULL );
noa = --last;
return args;
} /* ----- end of function parsecommand ----- */
/*
* === FUNCTION ======================================================================
* Name: find
* Description: Takes an array of character pointers and returns the index of the first
* occurence of a string. Assumes the last element of the array to be NULL.
* =====================================================================================
*/
int find ( char **args, const char *to_find )
{
int i;
for( i = 0; args[ i ] != NULL; i++)
if( !strcmp( args[ i ], to_find ) )
return i;
return 0;
} /* ----- end of function find ----- */
/*
* === FUNCTION ======================================================================
* Name: substr
* Description: Take a string and number of elements and return a substring of that length.
* =====================================================================================
*/
char* substr( const char *str, int elements ){
char *sub = malloc( ( elements * sizeof( char ) ) + 1 );
strncpy( sub, str, elements );
sub[elements] = '\0';
return sub;
}
/*
* === FUNCTION ======================================================================
* Name: strcpya
* Description: Allocates required memory and then copies the src to the dest.
* =====================================================================================
*/
char* strcpya ( const char *src )
{
char *dst = malloc( ( strlen( src ) + 1 ) * sizeof( char ) ); //length + \0
strcpy(dst, src);
return ;
} /* ----- end of function strcpya ----- */
char** getargc ( char **args ) {
int rid;
if( ( rid = find( args, "<" ) ) ||
( rid = find( args, ">" ) ) ||
( rid = find( args, ">>") )
){
int i;
char **argc = malloc( rid * sizeof(char*) );
for( i = 0; i < rid; i++ ){
argc[ i ] = args[ i ];
}
argc = realloc( argc, rid * sizeof( char* ) + 1 );
argc[ i ] = NULL;
return argc;
}
return args;
} /* ----- end of function getargc ----- */
char*** separate_piped_commands(char **args, int *no_of_piped_commands)
{
//printf("in separate, argscount is %d, args[0] is %s,", argscount, args[0]);
char*** piped_commands = (char*** )malloc(5 * sizeof(char**));
int k = 0;
int i,j=0;
piped_commands[k] = (char** ) malloc(20 * sizeof(char*));
//printf("in separate, argscount is %d, args[0] is %s, k is", argscount, args[0], k);
for (i = 0; args[i] != NULL; i++)
{
if (strcmp(args[i], "|") == 0)
{
k++;
j=0;
piped_commands[k] = (char** ) malloc(20 * sizeof(char*));
}
else
{
piped_commands[k][j] = (char* )malloc(100 *sizeof(char));
strcpy(piped_commands[k][j], args[i]);
j++;
}
}
//printf("%s",piped_commands[0][0]);
//printf("%s",piped_commands[0][1]);
*no_of_piped_commands = k;
return piped_commands;
} /* ----- end of function separate_piped_commands ----- */
void recursive_pipes(char*** piped_commands, int no_of_pipes_left)
{
// //ps | sort | less
if (no_of_pipes_left < 0)
{
return;
}
int fds[2];
pipe(fds);
int pid = fork();
if (pid == 0)
{
printf("hi");
close(1);
dup(fds[1]);
close(fds[0]);
recursive_pipes(piped_commands, no_of_pipes_left - 1);
close(fds[1]);
execvp(piped_commands[no_of_pipes_left - 1][0], piped_commands[no_of_pipes_left - 1]);
}
else if (pid > 0)
{
if (no_of_pipes_left > 0)
{
close(0);
dup(fds[0]);
}
close(fds[1]);
wait(NULL);
close(fds[0]);
execvp(piped_commands[no_of_pipes_left][0], piped_commands[no_of_pipes_left]);
perror("Command not found");
}
return;
} /* ----- end of function recursive_pipe ----- */
void sigint_handler( ){
signal( SIGINT, sigint_handler );
}
int main(){
//set signals
signal( SIGINT, sigint_handler );
//shell builtins
char *builtin[] = { "pwd", "cd" };
char *command = NULL;
//initialize readline's history session
using_history();
if( read_history( HISTFILE ) ){ //error reading history file
if( errno == ENOENT ){ //file not found
//silently create one
open( HISTFILE, O_CREAT | O_RDWR, 0644 );
}
else{ //other error
//simply display it
perror( "Error reading history file" );
}
}
//accept input till 'exit'
while( 1 ){
//read from the standard input
command = readline( "$ " );
if( !command ){ //EOF
write_history( HISTFILE ); //save the history
exit( EXIT_SUCCESS );
}
else if( !*command ){ //blank line
continue; //move to next iteration
}
else{ //text entered
add_history( command ); //add it to the history
}
if( strcmp( command, "exit" ) ){
write_history( HISTFILE ); //save the history
exit( EXIT_SUCCESS );
}
else{
int background = is_background( command );
pid_t pid = fork();
if( pid == 0 ){//in child
}
else{ //in parent
}
}
}
}
int is_background( char* command ){
}