yeban
3/6/2012 - 10:38 AM

Implementing a shell. External commands, builtins, I/O redirection, and pipes.

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 ){
}