dsb
dsb

Reputation: 121

Implementing history commad and rerunning old commands in own c shell

I am making my own c shell. The shell has a history function that displays the past commands. I have gotten that far, but I am having trouble rerunning old commands. I am trying to rerun the very last command when the user enter in rr. The problem is marked in the main function when rr is = args[0].

Here is my code:

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

#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
#define HIST_SIZE 12

int place=0;

char cmd[MAX_LINE+1]; //used to keep copy of inBuffer to put in *hist

/** The setup() routine reads in the next command line string storing it in the input buffer.
The line is separated into distinct tokens using whitespace as delimiters. Setup also 
modifies the args parameter so that it holds points to the null-terminated strings which 
are the tokens in the most recent user command line as well as a NULL pointer, indicating the
end of the argument list, which comes after the string pointers that have been assigned to
args. ***/

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

    int length,  /* #  characters in the command line */
    start,   /* Beginning of next command parameter           */
    i,       /* Index for inputBuffer arrray          */
    j;       /* Where to place the next parameter into args[] */

    /* Read what the user enters */
    length = read(STDIN_FILENO, inputBuffer, MAX_LINE);  

    strcpy(cmd,inputBuffer);

    start = -1;
    j = 0;

    if (length == 0)
       exit(0);            /* Cntrl-d was entered, end of user command stream */

    if (length < 0){
       perror("error reading command");
       exit(-1);           /* Terminate with error code of -1 */
    }

    /* Examine every character in the input buffer */
    for (i = 0; i < length; i++) {

    switch (inputBuffer[i]){
        case ' ':
        case '\t' :          /* Argument separators */

            if(start != -1){
                args[j] = &inputBuffer[start];    /* Set up pointer */
                j++;
            }

            inputBuffer[i] = '\0'; /* Add a null char; make a C string */
            start = -1;
            break;

        case '\n':             /* Final char examined */
            if (start != -1){
                args[j] = &inputBuffer[start];
                j++;
            }

            inputBuffer[i] = '\0';
            args[j] = NULL; /* No more arguments to this command */
            break;

       case '&':
           *background = 1;
           inputBuffer[i] = '\0';
           break;

       default :             /* Some other character */
           if (start == -1)
               start = i;
      }

  }    
  args[j] = NULL; /* Just in case the input line was > 80 */
} 

int displayHistory(char *hist[], int place){
   int i=place;
   int hist_num=1;

   do{
       if(hist[i]){
          printf("%d %s", hist_num, hist[i]);
          hist_num++;
       }
       i=(i+1)%HIST_SIZE;
   }  while(i != place);
   return 0;
}


int main(void){
    char inputBuffer[MAX_LINE]; /* Input buffer  to hold the command entered */
    char *args[MAX_LINE/2+1];/* Command line arguments */
    int background;             /* Equals 1 if a command is followed by '&', else 0 */

   char *hist[HIST_SIZE];
   int i=0;
   for(i=0; i<HIST_SIZE; i++){
      hist[i]=NULL;
   }

   while (1){            /* Program terminates normally inside setup */

       background = 0;

       printf("COMMAND--> ");  /* Shell prompt */
       fflush(0);

       setup(inputBuffer, args, &background);       /* Get next command */

       if(strcmp(args[0],"rr")==0){
            //rr does not go into *hist
       }
       else{
           cmd[strlen(cmd)]='\0';
           free(hist[place]);  
           hist[place]=strdup(cmd); 
           place=(place+1)%HIST_SIZE;
      }
     /* Fill in the code for these steps:  
    (1) Fork a child process using fork(),
    (2) The child process will invoke execvp(),
    (3) If background == 0, the parent will wait, 
   o/w returns to the setup() function. */

    pid_t pid=fork(); 
    int status; 
    if(pid<0){ //error forking 
       fprintf(stderr, "Fork Failed"); 
       exit(-1); 
    } 
    else if(pid==0){ //child process  
        if(strcmp(inputBuffer,"h")==0){
        displayHistory(hist,place);
    }
        else if(strcmp(args[0],"rr")==0){
            printf("%s",hist[place-1]); //this is here to show that hist[place-1] does have the last command
            execvp(hist[place-1], args);  //PROBLEM HERE. why doesn't the hist[place-1]get executed?
        }
        else{
            if(execvp(inputBuffer, args)<0){ 
                printf("Invalid Command\n"); 
            }
        }
    } 
    else{ //parent process 
       if(background==0){ //wait 
           waitpid(pid, &status, 0); 
       } 
    }
    memset(inputBuffer,0,sizeof(inputBuffer)); //this is done so garbage chars from old input does not show up later when history is displayed
    memset(cmd, 0, sizeof(cmd));
  }
}

An explanation of why the old command is not executed would be nice. And a way to actually get the command to run would be great.

Upvotes: 0

Views: 1137

Answers (1)

Weather Vane
Weather Vane

Reputation: 34585

To advance the circular buffer index, you correctly use

place = (place + 1) % HIST_SIZE

But you step back incorrectly with

hist[place - 1]

This should be

hist [ (place + HIST_SIZE - 1) % HIST_SIZE ]

Upvotes: 1

Related Questions