0

Everything seemed to be working fine when I was forking and calling execvp() outside of the setup() function. However I want to detect whether the input is a valid command, and return an integer if the command is valid. That way I can add the command to my history if it is a valid command. I can then run commands from history using "r N", where N is an arbitrary number, and then re-store that command (not "r N") in my history again. Before I moved fork() into the setup() function everything worked the way I wanted. Now that I'm forking and running commands inside setup() two things are happening that I don't understand.

1) If I type a command and some flags, only the command is stored in history and the flags are chopped off. I don't see where the flags are being removed.
2) If I type "r N" to run the Nth command in history, "r N" is stored back into history instead of the Nth command.

So for example:
Before I moved fork() and execvp() into setup() I could type ls -al & and the entire string would be stored into *history[]. Now only ls is being stored. Then if I wanted to rerun that command I could type
r 1 and ls -al & would be added to the *history[] array again. Now r 1 is being added to the history.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> // to use system calls
#include <string.h> // to use string functions
#include <signal.h> // to handle Ctrl-C

#define MAX_LINE 80
#define BUFFER_SIZE 50
char *history[MAX_LINE];    // a list to hold each command entered
int cmdNum = 0;             // keep track of the number of commands entered

void handle_SIGINT() {      // if control C is detected then list the last 10 input commands
    int displayNum = cmdNum;
    printf("\n");
    int i = 10;
    int j = cmdNum -1;
    while(i > 0 && j > -1) {
        printf("%d %s\n", displayNum, history[j]);
        i--;
        j--;
        displayNum--;
    }
}

/* setup() reads in the next command line, separateing it into distinct
 * tokens using whitespace as delimiters. setup() modifies the args
 * parameter so that it holds pointers to the null-terminated strings
 * that are the tokens in the most recent user command line as well
 * as NULL pointer, indicating the end of the argument list. setup()
 * calls exit() when Controld-D is entered. */

int setup(char inputBuffer[], char *args[], int *background) {

    char *hasInput = fgets(inputBuffer, MAX_LINE, stdin);   // grab user input
    char *arguments;                                        // temporary string to hold input as it's split into separate strings
    int i = 0;                                              // place holder for moving forward in args[]


    char last = inputBuffer[(strlen(inputBuffer)-1)];       // check the last character in inputBuffer
    if(last == '&') {                                       // is the last character '&'
        *background = 1;
    }

    if(hasInput == NULL) {                                  // check for control D and exit
        printf("\n");
        exit(EXIT_SUCCESS);
    } else if(strlen(inputBuffer) < 2) {                    // is inputBuffer empty?
        return 0;
    } else {
        inputBuffer = strtok(inputBuffer, "\n");            // remove \n from the end of the inputBuffer
        char *forHistory = strdup(inputBuffer);             // make a copy of the input because its original value gets modified
        char *tempInput = strdup(inputBuffer);              // copy input again because it gets modified after strtok
        arguments = strtok(tempInput, " \n");               // get the first string from inputBuffer before a " " appears
        if (*arguments == 'r') {                            // is the first string just the character 'r'?
            arguments = strtok(NULL, " \n");                // resume splitting input buffer from where it left off
            char *safetyString;
            if((strtol(arguments, &safetyString, 10) -1) > -1 &&        // take the next argument after the letter r and turn it into an integer
                    (strtol(arguments, &safetyString, 10) -1) <= cmdNum -1 &&
                    history[0] != 0) {
                int cmdToRun = strtol(arguments, &safetyString,10) - 1; // subtrack 1 from the integer since arrays start at 0
                inputBuffer = strdup(history[cmdToRun]);                // inputBuffer should be equal to a runnable command from history and not "r #"
                forHistory = strdup(history[cmdToRun]);                 // change forHistory as well, because we don't want to save "r #" in history
                printf("forHistory: %s\n", forHistory);
            } else {
                printf("Command not in history.\n");
                return 0;
            }
        }

        if(*background == 1) {                              // remove the '&' from inputBuffer before storing each string in args
            inputBuffer[(strlen(inputBuffer)-1)] = 0;
        }

        arguments = strtok(inputBuffer, " \n");             // get the first string from inputBuffer before a " "
        while(arguments != NULL) {
            args[i] = arguments;                            // place each space separated string into args
            arguments = strtok(NULL, " \n");                // resume splitting inputBuffer from where it left off
            i++;
        }
        args[i] = 0;                                        // add the null terminator to args

        pid_t pid;
        int status;

        if((pid = fork()) < 0) {            // make sure fork() works
        } else if(pid == 0) {               // if this is a child process
            if(execvp(args[0], args) < 0) { // try to execute command
                // if the program gets here then execvp() failed
                printf("execvp failed, ivalid command\n");
                return 0;
                //exit(EXIT_FAILURE);
            }
        } else if(*background == 0) {       // unless specified wait for the the child to finish
            while(wait(&status) != pid);
            inputBuffer = strdup(forHistory);
            //printf("inputBuffer after = forHistory: %s\n", inputBuffer);
            return 1;
        }
    }
}

int main(void) {
    char inputBuffer[MAX_LINE];     // buffer to hold command entered
    int background;                 // equals 1 if a command is folled by '&'
    char *args[MAX_LINE/2+1];       // command line arguments

    while(1) {
        background = 0;
        printf("COMMAND->");

        struct sigaction handler;
        handler.sa_handler = handle_SIGINT;
        sigaction(SIGINT, &handler, NULL);
        siginterrupt(SIGINT, 0);


        if(setup(inputBuffer, args, &background)) {
            //printf("inputBuffer before history placement: %s\n", inputBuffer);
            history[cmdNum] = strdup(inputBuffer);              // copy the comand to history
            cmdNum++;
        }
        /* the steps are:
         * (1) fork a child process using fork()
         * (2) the child process will invoke execvp()
         * (3) if background == 0, the parent will wait,
         * otherwise it will invoke the setup() function again */
    }
}
Van
  • 169
  • 1
  • 3
  • 11
  • 1
    `void handle_SIGINT(void) {}` should be `void handle_SIGINT(int signum) {...}` And you should not call printf() from within a signal handler. – wildplasser Oct 01 '14 at 09:05
  • (1) Your unexpected behaviour has nothing to do with `fork`. Use a debugger to solve this. (2) You cannot report a successful `exec`, so you cannot store just the successful commands in your history. Store all commands. – n. m. could be an AI Oct 01 '14 at 10:58
  • @n.m.Ok, so when I tried `ls -al` then checked `inputBuffer[4]` in main() with printf() it prints `a` as to be expected. So it is not fork(), but I assume a problem with my assignment `history[cmdNum] = strdup(inputBuffer)`. I'm trying to assign a char array to a pointer. Is there a way to convert from char array to pointer? – Van Oct 01 '14 at 12:07
  • Your program is a horrible mess of pointer/array confusion and memory mishandling. It is basically all terminally wrong from the beginning through the middle to the end. I don't know how to fix it other than by throwing it all away and starting over. Sorry. – n. m. could be an AI Oct 01 '14 at 19:56
  • the function: strdup() is being used extensively, however; strdup() allocates memory. so that memory needs to be free() before leaving the program. – user3629249 Oct 02 '14 at 08:12
  • @n.m. This is my first time writing anything in C. I guess jumping in without any prior knowledge isn't the right way of going about things. Thanks for at least looking at my question. – Van Oct 02 '14 at 20:36
  • "isn't the right way of going about things" Well I agree with that. Take much smaller steps. You are trying to do too many new things at once, no wonder you are getting lost. Get a good C book, read it, do exercises. http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list should get you started. – n. m. could be an AI Oct 02 '14 at 20:54

0 Answers0