Ilustración 1 Executing The Program
Ilustración 1 Executing The Program
accepts user commands and then executes each command in a separate process. A shell interface provides the user a prompt after which the next command is entered.
Ilustracin 2 Executing gedit command, waits for the child process to terminate
UNIX shells typically also allow the child process to run in the background-or concurrentyl-as well by specifying the ampesand (&) at the end of the command. Example:
This program is terminated when the user enters <Control><D> and setup() theb invokes exit(). The contents of the command entered by the user is loaded into the args array.
Creating a History Feature This code also provides a history feature that allows the user access up to the 10 most recently entered commands. These commands will be numbered starting at 1 and will continue to grow larger even past 10, e.g. if the user has entered 35 commands, the 10 most recent commands sholud be numbered 26 to 35. The user will be able to list these commands when he/she presses <Control><C>, which is the SIGINT signal. When the user presses that command, the signal handler will output a list of the most recent 10 commands.
With the list, the user can run any of the previous 10 commands by entering r x where 'x' is the first letter of that command. If more than one command starts with 'x', execute the most recent one.
Ilustracin 8 Executing "r e", searching the last command with initial the letter e
Ilustracin 9 Executing "r l", searching the last command with initial letter 'l'
Also, the user should be able to run the most recent command again by just entering 'r'. Any command that is executed in this fashion will be echoed on the user's screen and the command is also placed in the history buffer as the next command.
Ilustracin 11 History is updated whe 'r' is executed and adds the command executed
If he user attempts to use this history facility to run a command and the command is detected to be erroneous, an error message should be given to the user and the command not entered in the history list, and the execvp() will not be called
char history[MAX_COMMANDS][MAX_LINE]; char display_history[MAX_COMMANDS][MAX_LINE]; static char buffer[BUFFER_SIZE]; int ctrC = 0; int command_count = 0; void addtohistory(char inputBuffer[]) { int i = 0; int limit_history = 0; if (command_count >= 9) { limit_history = 9; } else { limit_history = command_count; } //shift all other commands in history[] if (command_count > 0) { for (i = limit_history; i > 0; i--) { /** * quitar este comentario para ver como va recorriendo los comandos printf("Copia [%d] %s a [%d] %s\n", i - 1, display_history[i - 1], i, display_history[i]); */ strcpy(history[i], history[i - 1]); strcpy(display_history[i], display_history[i - 1]); } } // add the command to beggining strcpy(history[0], inputBuffer); // add the display-style command to beginning history i = 0; while (inputBuffer[i] != '\n' && inputBuffer[i] != '\0') { display_history[0][i] = inputBuffer[i]; i++; } display_history[0][i] = '\0'; //make a C string
++command_count; return; } /* the signal handler function */ void handle_SIGINT() { int i, upper; //write(STDOUT_FILENO,buffer,strlen(buffer)); if (command_count < MAX_COMMANDS){ upper = command_count; } else { upper = MAX_COMMANDS; } //Desplegar 10 comandos recientes en pantalla printf("\n---Command History---\n"); for (i = 0; i < upper; i++) { printf("%d \t %s\n", command_count - i, display_history[i]); } ctrC = 1; //exit(0); } /** setup() reads in the next command line, separating 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 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; /* # of characters in the command line */ int i; /* loop index for accessing inputBuffer array */ int start; /* index where beginning of next command parameter is */ int ct; /* index of where to place the next parameter into args[] */ char command_firstl; /* first letter of command */ ct = 0; /* read what the user enters on the command line */ length = read(STDIN_FILENO, inputBuffer, MAX_LINE); //printf("First size: %zu\n",strlen(inputBuffer));//QUITAR //printf("length: %d\n",length); start = -1; if(length == 0) exit(0); /* ^d was entered, end of user command stream */ if(length < 0){ //perror("error reading the command"); //exit(-1); /* terminate with error code of -1 */ } /* flag so it doesn't add command to history */ /* exits program */
/** * Check if they are using history */ if (inputBuffer[0] == 'r') { //printf("\nI received R\n"); if (command_count == 0) { printf("No history\n"); //return 1; } else if (inputBuffer[1] == '\n') { // restore the previous command int index = 0; strcpy(inputBuffer, history[index]); //echoes command on screen printf("COMMAND -> %s\n", display_history[0]); length = strlen(inputBuffer) + 1; } else if (inputBuffer[1] == ' ') { /* checks for r followed by a letter */ if (isalpha(inputBuffer[2])) { /* If is letter (alphabetic) */ int i = 0; for (i = 0; i < 10; i++) { command_firstl = history[i][0]; /* extract first letter of command in history */ if (command_firstl == inputBuffer[2]) { strcpy(inputBuffer, history[i]); //echoes command on screen printf("COMMAND -> %s\n", display_history[i]); length = strlen(inputBuffer) + 1; break; } } if (i >= 10) { /* didn't match any beggining letter */ strcpy(inputBuffer, "echo No hay comandos que comiencen con esa letra\n"); length = strlen(inputBuffer) + 1; ctrC = 1; /* don't add to history */ } } else { printf("Error, ingrese la primera letra del comando\n"); } } else { printf("Error, deje un espacio entre letras\n"); } } ctrC ? ctrC = 0 : addtohistory(inputBuffer); /* examine every character in the inputBuffer */ for(i = 0; i < length; i++){ switch(inputBuffer[i]){ case ' ': case '\t': /* argument separators */ if(start != -1){
/* set up pointer at beggining of command or command option */ args[ct] = &inputBuffer[start]; ct++; /* increase counter */ } inputBuffer[i] = '\0'; /* add a null char; make a C string*/ start = -1; break; case '\n': */ if(start != -1){ args[ct] = &inputBuffer[start]; ct++; } inputBuffer[i] = '\0'; args[ct] = NULL; /* no more arguments to this command */ break; default : /* some other character */ if(start == -1) start = i; if(inputBuffer[i] == '&'){ *background = 1; inputBuffer[i] = '\0'; } } } args[ct] = NULL; /* just in case the input line was > 80*/ } int main(void) { char inputBuffer[MAX_LINE]; /* buffer to hold command entered */ int background; /* equals 1 if a command is followed by '&' */ char *args[MAX_LINE/1]; /* command line (of 80) has max of 40 arguments */ /*code made by CREW ISD*/ int ppid,pid; /*code made by CREW ISD*/ /* set up the signal handler */ struct sigaction handler; handler.sa_handler = handle_SIGINT; handler.sa_flags = 0; sigemptyset(&handler.sa_mask); sigaction(SIGINT, &handler, NULL); strcpy(buffer,"Caught <ctrl><c>\n"); while(1){ background = 0; ppid = getpid(); //printf("Parent: %d\n",ppid); printf("COMMAND -> "); /* should be the final char examined
fflush(stdout); /* setup() calls exit() when Control-D is entered */ setup(inputBuffer, args, &background); /** code made by CREW ISD */ pid = fork(); if(pid == 0){ execvp(args[0],args); if (inputBuffer[0] != 'r') { vlido printf("Error: Unknown command, please try again\n"); exit(-1); } } else { if(background == 0){ //wait(NULL); //printf("\nWaiting for the child: %d to terminate",pid); wait(NULL); //printf("\nEND WAITING\n"); //printf("Child: %d\n",pid); }else{ //exit(0); printf("\nbackg = 1, running child: %d in parallel with parent: %d\n", pid, ppid); //printf("Printing contents: %c\n",*args[0]); } } /** code made by CREW ISD */ /* printf(args[0]); printf(args[1]); printf("%d\n",background); */ /** the steps are: (1) fork a child process using fork() (2) the child process will invoke execvp() (3) if background == 1, the parent will wait, otherwise it will invoke the setup() function again. */ } }