+1 (315) 557-6473 

Create A Linux Shell Replacement Using C Assignment Solutions.


Instructions

Objective
Write a C homework to create a Linux shell replacement using C

Requirements and Specifications

In this assignment, you will implement the shell “engine” as the “group” component, where all members are responsible for the following functionality:
A Command-Line Interpreter, or Shell
Your shell should read the line from standard input (i.e., interactive mode) or a file (i.e., batch mode), parse the line with command and arguments, execute the command with arguments, and then prompt for more input (i.e., the shell prompt) when it has finished.
  1. Interactive Mode
  2. In interactive mode, you will display a prompt (any string of your choosing) and the user of the shell will type in a command at the prompt.

  3. Batch Mode
In batch mode, your shell is started by specifying a batch file on its command line. The batch file contains the list of commands that should be executed. In batch mode, you should not display a prompt, but you should echo each line you read from the batch file back to the user before executing it.
You will need to use the fork() and exec() family of system calls. You may not use the system() system call as it simply invokes the system’s /bin/bash shell to do all of the work.
You may assume that arguments are separated by whitespace. You do not have to deal with special characters such as ', ", \, etc. However, you will need to handle the redirection operators (< and >) and the pipeline operator (|), which will be specified in the “individual” portion of this assignment.
Each line (either in the batch file or typed at the prompt) may contain multiple commands separate with the semicolon (;) character. Each command separated by a ; should be run sequentially, but the shell should not print the next prompt or take more input until all of these commands have finished executing (the wait() or waitpid() system calls may be useful here).
You may assume that the command-line a user types is not longer than 512 bytes (including the '\n'), but you should not assume that there is any restriction on the number of arguments to a given command.
Screenshots of output
Create a Linux shell replacement using C language
Source Code
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_LINE 512
char *PROMPT = "prompt> ";
typedef struct node
{
    char *val;
    struct node *next;
}node;
typedef struct
{
    node *path;
    char history[20][512];
    char lastcommand[512];
    int nhist;
    int starthist;
    int endhist;
}shell_env;
typedef struct
{
    int infd;
    int outfd;
}command_t;
void *my_malloc(size_t size);
void *my_realloc(void *ptr, size_t size);
void insert(node **list, char *str);
void delete(node **list, char *str);
void destroy(node *list);
char *remove_whitespace(char *str);
char **split_string(char *str, char *separator);
int num_elements(char **array);
void cd_cmd(int argc, char **argv);
void path_cmd(int argc, char **argv, node **path);
void myhistory_cmd(int argc, char **argv, shell_env *shenv);
int execute1(char *cmdline, shell_env *shenv, command_t cmd_data);
int execute_line(char *line, shell_env *shenv);
node *get_path();
char *build_path(node *list);
int main(int argc, char **argv)
{
    int terminate = 0;
    char buffer[MAX_LINE];
    char *line;
    int n;
    int interactive = 1;
    int fd = STDIN_FILENO;
    shell_env shenv;
    shenv.path = NULL;
    shenv.nhist = 0;
    shenv.starthist = 0;
    shenv.endhist = 0;
    if (argc == 2) /* batch file given */
    {
        fd = open(argv[1], O_RDONLY);
        if (fd < 0)
        {
            fprintf(stderr, "Error: Unable to open batch file!\n");
            return 1;
        }
        interactive = 0;
    }
    else if (argc > 2) {
        fprintf(stderr, "Error: Too many arguments!\n");
        return 1;
    }
    signal (SIGQUIT, SIG_IGN); /* ignore quit signal */
    signal (SIGTSTP, SIG_IGN); /* ignore stop signal */
    signal (SIGTTIN, SIG_IGN);
    signal (SIGTTOU, SIG_IGN);
    signal (SIGCHLD, SIG_IGN);
    signal (SIGINT, SIG_IGN); /* ignore interrupt signal */
    setpgid(0,0); /* set group to pid */
    tcsetpgrp (STDIN_FILENO, getpgrp()); /* set shell as foreground */
    shenv.path = get_path(); /* load the current path */
    while (!terminate)
    {
        if (interactive)
            write(STDOUT_FILENO, PROMPT, strlen(PROMPT));
        n = read(fd, buffer, MAX_LINE);
        if (n <= 0) /* if ctrl-d or EOF */
            terminate = 1;
        else
        {
            buffer[n] = 0;
            line = remove_whitespace(buffer); /* remove whitespace */
            if (line[0] != 0) /* ignore empty lines */
            {
                strcpy(shenv.lastcommand, line);
                terminate = execute_line(line, &shenv);
                if (shenv.lastcommand[0]) {
                    strcpy(shenv.history[shenv.endhist], shenv.lastcommand);
                    shenv.endhist++;
                    shenv.endhist %= 20;
                    shenv.nhist++;
                    if (shenv.nhist > 20)
                    {
                        shenv.nhist = 20;
                        shenv.starthist++;
                        shenv.starthist %= 20;
                    }
                }
            }
        }
    }
    if (!interactive)
        close(fd);
    destroy(shenv.path);
    return 0;
}
/* handle errors for malloc */
void *my_malloc(size_t size)
{
    void *ptr;
    if((ptr = malloc(size)) == NULL)
    {
        fprintf(stderr, "Error: out of memory!\n");
        exit(1);
    }
    return ptr;
}
/* handle errors for realloc */
void *my_realloc(void *ptr, size_t size)
{
    if((ptr = realloc(ptr, size)) == NULL)
    {
        fprintf(stderr, "Error: out of memory!\n");
        exit(1);
    }
    return ptr;
}
/* insert a string on the list */
void insert(node **list, char *str)
{
    node *newnode, *tmp;
    newnode = (node*) my_malloc(sizeof(node));
    newnode->val = str;
    newnode->next = NULL;
    if (*list == NULL)
        *list = newnode; /* set as new start of list */
    else
    {
        tmp = *list;
        while(tmp->next) /* add at end of list*/
            tmp = tmp->next;
        tmp->next = newnode;
    }
}
/* deletes a string from the list */
void delete(node **list, char *str)
{
    node *tmp, *prev;
    if (*list == NULL)
        return;
    else
    {
        tmp = *list;
        prev = NULL;
        while(tmp) {
            if (!strcmp(str, tmp->val)) /* if we found the value */
                break;
            prev = tmp;
            tmp = tmp->next;
        }
        if (tmp == NULL) /* not found */
            return;
        if (prev == NULL) /* removing first node */
        {
            printf("%s\n", (*list)->val);
            *list = tmp->next;
            printf("%s\n", tmp->val);
            printf("%s\n", (*list)->val);
        }
        else
            prev->next = tmp->next;
        free(tmp->val);
        free(tmp);
    }
}
/* destroys the list and frees all memory used by it */
void destroy(node *list)
{
    node *ptr, *tmp;
    if (list != NULL) {
        ptr = list;
        while(ptr)
        {
            tmp = ptr;
            ptr = ptr->next;
            if (tmp->val != NULL)
                free(tmp->val);
            free(tmp);
        }
    }
}
/*
    Remove all the white space
*/
char *remove_whitespace(char *str)
{
    int i = 0, j;
    /* remove initial spaces */
    while (str[i] && isspace(str[i]))
        i++;
    if (str[i] == 0) /* if empty string */
        return str + i; /* return empty string */
    j = strlen(str) - 1; /* end of string */
    /* remove ending whitespaces */
    while (j >= 0 && isspace(str[j]))
        j--;
    str[j + 1] = 0; /* remove last whitespace*/
    return str + i;
}
/* split string using the given separator */
char **split_string(char *str, char *separator)
{
    char **toks = NULL;
    char *token;
    int n = 0;
    toks = (char **) my_malloc(sizeof(char *));
    token = strtok(str, separator);
    while (token != NULL)
    {
        toks[n++] = token;
        toks = (char **) my_realloc(toks, (n+1)*sizeof(char *));
        token = strtok(NULL, separator);
    }
    toks[n] = NULL;
    return toks;
}
/* count the number of elements in pointer array */
int num_elements(char **array)
{
    int n = 0;
    while (array[n])
        n++;
    return n;
}
/* execute the built-in command cd */
void cd_cmd(int argc, char **argv)
{
    char *home;
    if (argc == 1)
    {
        home = getenv("HOME");
        if (home == NULL)
            fprintf(stderr, "Error: HOME variable is not set\n");
        else if (chdir(home) < 0)
            fprintf(stderr, "Error: Unable to change to HOME\n");
    }
    else if (chdir(argv[1]) < 0) /* change to the directory given */
        fprintf(stderr, "Error: Unable to change to %s\n", argv[1]);
}
/* execute the built-in command path */
void path_cmd(int argc, char **argv, node **path)
{
    char *curpath;
    if (argc == 1)
    {
        curpath = build_path(*path); /* get path as a string */
        printf("PATH=%s\n", curpath);
        free(curpath);
    }
    else if (!strcmp(argv[1],"-"))
    {
        if (argc < 3)
            fprintf(stderr, "Error: missing path to remove\n");
        else
            delete(path, argv[2]);
    }
    else if (!strcmp(argv[1],"+"))
    {
        if (argc < 3)
            fprintf(stderr, "Error: missing path to add\n");
        else
            insert(path, strdup(argv[2]));
    }
    else
    {
        fprintf(stderr, "Error: Invalid option for path\n");
    }
}
/* execute the built-in myhistory path */
void myhistory_cmd(int argc, char **argv, shell_env *shenv)
{
    int i, j, n;
    char cmd[512];
    if (argc == 1)
    {
        i = shenv->starthist;
        for (j = 0; j < shenv->nhist; j++)
        {
            printf("%d %s\n", j+1, shenv->history[i]);
            i++;
            i %= 20;
        }
    }
    else if (!strcmp(argv[1],"-c"))
    {
        shenv->nhist = 0;
        shenv->starthist = 0;
        shenv->endhist = 0;
    }
    else if (!strcmp(argv[1],"-e"))
    {
        if (argc < 3)
            fprintf(stderr, "Error: missing command number\n");
        else
        {
            n = atoi(argv[2]); /* get command number */
            if (n < 1 || n > shenv->nhist)
            {
                fprintf(stderr, "Error: Invalid command number\n");
                shenv->lastcommand[0] = 0;
            }
            else
            {
                i = ((n - 1) + shenv->starthist) % 20;
                strcpy(cmd, shenv->history[i]);
                execute_line(cmd, shenv);
                strcpy(shenv->lastcommand, shenv->history[i]); /* replace command */
            }
        }
    }
    else
    {
        fprintf(stderr, "Error: Invalid option for myhistory\n");
    }
}
/* executes a command, returns 1 if it was exit */
int execute1(char *cmdline, shell_env *shenv, command_t cmd_data)
{
    char **args;
    int i;
    int is_exit = 0;
    pid_t pid;
    int status;
    char *path;
    args = split_string(cmdline, " "); /* get command and arguments */
    if (!strcmp(args[0], "cd")) /* if cd */
        cd_cmd(num_elements(args), args); /* execute cd */
    else if (!strcmp(args[0], "exit")) /* if exit */
    {
        is_exit = 1;
    }
    else if (!strcmp(args[0], "path")) /* if path */
        path_cmd(num_elements(args), args, &shenv->path); /* execute path */
    else if (!strcmp(args[0], "myhistory")) /* if myhistory */
    {
        myhistory_cmd(num_elements(args), args, shenv); /* execute history */
    }
    else /* maybe an external program */
    {
        pid = fork();
        if (pid == -1)
            fprintf(stderr, "Error: unable to fork process!\n");
        else {
            if (pid == 0) /* child process */
            {
                signal(SIGQUIT, SIG_DFL); /* set default signal handler */
                signal(SIGTTIN, SIG_DFL);
                signal(SIGTTOU, SIG_DFL);
                signal(SIGTSTP, SIG_DFL);
                signal(SIGCHLD, SIG_DFL);
                signal(SIGINT, SIG_DFL);
                setpgid(0,0); /* set group id to pid*/
                tcsetpgrp(STDIN_FILENO, getpgrp()); /* set process in foreground */
                if (cmd_data.infd != -1) /* if we are in a pipe */
                {
                    dup2(cmd_data.infd, STDIN_FILENO); /* use pipe for stdin */
                    close(cmd_data.infd); /* close duplicate */
                }
                if (cmd_data.outfd != -1) /* if we are in a pipe */
                {
                    dup2(cmd_data.outfd, STDOUT_FILENO); /* use pipe for stdout */
                    close(cmd_data.outfd); /* close duplicate */
                }
                path = build_path(shenv->path); /* get path as a string */
                setenv("PATH", path, 1); /* update path */
                free(path);
                if (execvp(args[0], args))
                {
                    fprintf(stderr, "Error: Invalid command \"%s\"\n", args[0]);
                    exit(1);
                }
            }
            setpgid(pid,pid); /* use child pid for group id */
            tcsetpgrp(STDIN_FILENO, getpgid(pid)); /* set child as foreground process */
            wait(&status);
        }
    }
    free(args);
    return is_exit;
}
/* execute a pipelined command line */
int execute_pipeline(char *line, shell_env *shenv)
{
    char **cmds;
    char *command;
    int i;
    int n;
    int is_exit = 0;
    int status;
    int **pipes;
    command_t cmd_data;
    char *filename;
    int fd;
    cmds = split_string(line, "|"); /* get all pipelined commands */
    n = num_elements(cmds);
    if (n == 1) /* single command */
    {
        command = remove_whitespace(cmds[0]);
        if (command[0] != 0) /* only execute non empty commands */
        {
            cmd_data.infd = -1;
            cmd_data.outfd = -1;
            if ((filename = strchr(command, '>')) != NULL) /* if there's output redirection */
            {
                filename[0] = 0; /* remove char */
                filename = remove_whitespace(filename + 1);
                fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                if (fd == -1)
                {
                    fprintf(stderr, "Error: Unable to open file \"%s\"", filename);
                    return 0;
                }
                cmd_data.outfd = fd;
            }
            else if ((filename = strchr(command, '<')) != NULL) /* if there's input redirection */
            {
                filename[0] = 0; /* remove char */
                filename = remove_whitespace(filename + 1);
                fd = open(filename, O_RDONLY);
                if (fd == -1)
                {
                    fprintf(stderr, "Error: Unable to open file \"%s\"", filename);
                    return 0;
                }
                cmd_data.infd = fd;
            }
            is_exit = execute1(command, shenv, cmd_data);
        }
    }
    else { /* several commands */
        pipes = (int**) my_malloc((n-1)*sizeof(int *)); /* create pipes for the processes */
        for (i = 0; i < n - 1; i++)
        {
            pipes[i] = (int *) my_malloc(2*sizeof(int));
            pipe(pipes[i]); /* create pipe */
        }
        for (i = 0; i
            if (i == 0)
                cmd_data.infd = -1;
            else
                cmd_data.infd = pipes[i - 1][0];
            if (i < n - 1)
                cmd_data.outfd = pipes[i][1];
            else
                cmd_data.outfd = -1;
            command = remove_whitespace(cmds[i]);
            if (command[0] != 0) /* only execute non empty commands */
                is_exit = execute1(command, shenv, cmd_data);
            if (i < n -1)
                close(pipes[i][1]);
            if (i != 0)
                close(pipes[i - 1][0]);
        }
        for (i = 0; i < n - 1; i++)
        {
            close(pipes[i][0]);
            close(pipes[i][1]);
            free(pipes[i]);
        }
        free(pipes);
    }
    free(cmds);
    return is_exit;
}
/* execute all commands in command line separated by ; */
int execute_line(char *line, shell_env *shenv)
{
    char **cmds;
    char *command;
    int i;
    int is_exit = 0;
    cmds = split_string(line, ";"); /* get all sequential commands */
    for (i = 0; i < num_elements(cmds); i++) /* execute commands one by one */
    {
        command = remove_whitespace(cmds[i]);
        if (command[0] != 0) /* only execute non empty commands */
            is_exit |= execute_pipeline(command, shenv);
    }
    free(cmds);
    tcsetpgrp(STDIN_FILENO, getpgrp()); /* return to shell in foreground*/
    return is_exit;
}
/* get the path variable and saves it in a linked list */
node *get_path()
{
    node *list = NULL;
    char **parts;
    int i;
    char *path = getenv("PATH");
    parts = split_string(path, ":"); /* split by : */
    for (i = 0; i < num_elements(parts); i++) /* save in linked list */
    {
        insert(&list, strdup(parts[i]));
    }
    free(parts);
    return list;
}
/* returns a string with the path from the list */
char *build_path(node *list)
{
    node *tmp;
    char *path;
    int size = 0;
    for (tmp = list; tmp != NULL; tmp = tmp->next)
        size += strlen(tmp->val) + 1;
    path = my_malloc(size + 1);
    path[0] = 0;
    for (tmp = list; tmp != NULL; tmp = tmp->next) /* append all paths using :*/
    {
        if (tmp != list)
            strcat(path, ":");
        strcat(path, tmp->val);
    }
    return path;
}