/**
 * Nanoshell 0.1
 * Copyleft 2016 Mc128k
 * Bianchi Giovani (#255815)
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#define VERSION "0.1"
#define BUF_MAX 2048

// Holds child PID, or 0 for the child itself, used in sighandler
int child_pid;

// Handle env variables, more portable than envp
extern char **environ;

// Used to avoid quitting the shell if a child program is interrupted
void catch_sigint(int signo);

int main(int argc, const char **argv) {
    char buffer[BUF_MAX];
    char *tok_ptr, *c_cmd;
    int last_status = 0;

    // No need to use sigaction, it's called once
    signal(SIGINT, catch_sigint);
    
    printf("Nanoshell %s - Copyleft 2016 Mc128k\n", VERSION);
    printf("Type 'help' if you get stuck\n");

    // Parser loop
    while(1) {
        child_pid = 0;
        printf("> ");

        // Read line from console
        fgets(buffer, BUF_MAX, stdin);

        // Parse and clean command
        c_cmd = strtok_r(buffer, " ", &tok_ptr);
        c_cmd[strcspn(c_cmd, "\r\n")] = 0; // Remove trailing newline
        //c_cmd = strtok(buffer, " ");

        if(strcmp(c_cmd, "help") == 0) {
            // Print help
            printf("  printenv  - Print environment variables\n");
            printf("  exit      - Exit nanoshell\n");
            printf("  help      - This help message\n");
            printf("  status    - Print last exit status\n");
            printf("\n");
        } else
        if(strcmp(c_cmd, "status") == 0) {
            // Print last exit status
            printf("Last status: %d\n", last_status);
        } else
        if(strcmp(c_cmd, "exit") == 0) {
            // Exit
            break;
        } else
        if(strcmp(c_cmd, "printenv") == 0) {
            // Print environment variables
            int i;
            for(i=0;environ[i];i++) {
                printf("  %s\n", environ[i]);
            }
        } else {
            // Just a command, parse arguments and run
            int c_argc = 1;
            char *tmp = tok_ptr;
            while(strtok_r(NULL, " ", &tmp)) 
                c_argc++;
            char *c_argv[c_argc+1];

            // Create a vector with arguments, correct last for fgets
            int i;
            c_argv[0] = c_cmd;
            for(i=1;i<c_argc;i++) {
                c_argv[i] = strtok_r(NULL, " ", &tok_ptr);
            }
            int j = strcspn(c_argv[i-1], "\r\n");
            c_argv[i-1][j] = 0; 
            c_argv[i] = (char *)0;
            
            // Create child process
            child_pid = fork();
            if(child_pid == -1) {
                perror("Fork error");
                exit(-1);
            } else
            if(child_pid == 0) {
                // Child process here
                execvp(c_cmd, c_argv);
                perror("exec");
                exit(-1);
            } else {
                // Parent process here
                int status;
                wait(&status);
                if(WIFEXITED(status)) {
                    last_status = WEXITSTATUS(status);
                } else {
                    printf("Process terminated abnormally\n");
                }
            }
        }            
    }
    return 0;
}

void catch_sigint(int signo) {
    // Clean line
    printf("\n");

    // Interrupt child
    if(child_pid > 0) {
        kill(child_pid, SIGINT);
    } else {
        exit(0);
    }
}
