You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
5.4 KiB
225 lines
5.4 KiB
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include "apostolofShellFunctions.h"
|
|
|
|
const char semicolonDelimeter[2] = ";";
|
|
const char ampersandDelimeter[2] = "&";
|
|
|
|
void interactiveMode(){
|
|
char input[BUFFER_SIZE];
|
|
int quit_called = 0;
|
|
|
|
while (1){
|
|
printf(GRN "\x1B[1mfanakis_8261> \x1B[0m" RESET);
|
|
//Gets user input and checks for errors
|
|
if (!fgets(input, BUFFER_SIZE, stdin) || feof(stdin) || ferror(stdin)){
|
|
continue;
|
|
}
|
|
//Checks for input buffer overflow
|
|
if ((strlen(input) == BUFFER_SIZE - 1) && input[BUFFER_SIZE - 2] != '\n'){
|
|
printf("Input too long! Please confine input to 512 characters.\n");
|
|
//Clears garbage from stdin
|
|
char discarded;
|
|
while ((discarded = fgetc(stdin)) != '\n' && discarded != EOF);
|
|
continue;
|
|
}
|
|
//Trims possible leading and trailing whitespace
|
|
strcpy(input, trimWhitespaces(input));
|
|
if (strcmp(input, "\n") == 0 || input == 0){
|
|
continue;
|
|
}
|
|
//Parses semicolon-separated chunks first
|
|
unconditionalRun(input, &quit_called);
|
|
if (quit_called == QUIT_CALLED){
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
void batchFileMode(char *filename){
|
|
char line[BUFFER_SIZE];
|
|
int quit_called = 0;
|
|
|
|
//Opens file
|
|
FILE* file = fopen(filename, "r");
|
|
if (!file){
|
|
printf("Couldn't open file.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
//Reads file one line at a time
|
|
while (fgets(line, sizeof(line), file)) {
|
|
strcpy(line, trimWhitespaces(line));
|
|
if (strcmp(line, "\n") == 0 || line == 0){
|
|
continue;
|
|
}
|
|
unconditionalRun(line, &quit_called);
|
|
if (quit_called == QUIT_CALLED){
|
|
fclose(file);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
fclose(file);
|
|
return;
|
|
}
|
|
|
|
void unconditionalRun(char *input, int *quit_called){
|
|
char *rest, *token;
|
|
|
|
//Parses chunks of input split by semicolons (";")
|
|
//Gets first token
|
|
token = strtok_r(input, semicolonDelimeter, &rest);
|
|
while (token != NULL){
|
|
//Further parses and conditionally runs chunks
|
|
conditionalRun(token, quit_called);
|
|
//Parses ampersand-separated chunks and runs them successively
|
|
if (*quit_called == QUIT_CALLED){
|
|
return;
|
|
} /*else if ((*rest == 0)){
|
|
return 1;
|
|
}*/
|
|
token = strtok_r(NULL, semicolonDelimeter, &rest);
|
|
}
|
|
}
|
|
|
|
void conditionalRun(char *input, int *quit_called){
|
|
char *rest, *token;
|
|
|
|
//Parses ampersand-separated chunks
|
|
//Gets first token
|
|
token = strtok_r(input, ampersandDelimeter, &rest);
|
|
while (token != NULL){
|
|
char *command, **argv;
|
|
int argc = 0;
|
|
|
|
//Parses the command
|
|
parseCommand(trimWhitespaces(token), &command, &argv, &argc);
|
|
|
|
if (strcmp(command, "quit") == 0){
|
|
*quit_called = QUIT_CALLED;
|
|
return;
|
|
}
|
|
|
|
//Executes the command
|
|
if (execute(command, argv)){
|
|
//Exits on failure because this is a conditional run
|
|
return;
|
|
}
|
|
|
|
//Cleans up space allocated
|
|
free(command);
|
|
for (int i=0; i<argc; ++i){
|
|
free(argv[i]);
|
|
}
|
|
free(argv);
|
|
|
|
//Checks if chunk isn't split by a double ampersand
|
|
if (strlen(rest) != 0){
|
|
if (!(rest[0] == '&' && isspace(rest[1]))){
|
|
printf("Not supported\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Gets next token
|
|
token = strtok_r(NULL, ampersandDelimeter, &rest);
|
|
}
|
|
}
|
|
|
|
int execute(char *command, char **argv){
|
|
pid_t pid;
|
|
int status = 0;
|
|
|
|
if ((pid = fork()) < 0) {
|
|
//Failed to create child process
|
|
perror("Can't fork");
|
|
exit(EXIT_FAILURE);
|
|
} else if (pid == 0) {
|
|
//Child process
|
|
if (execvp(command, argv)) {
|
|
perror("Error executing command");
|
|
_exit(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
//Parent process
|
|
while (wait(&status) != pid)
|
|
;
|
|
return status;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
void parseCommand(char *input, char **command, char ***argv, int *argc){
|
|
char *token = strtok(input, " ");
|
|
*argc = 1;
|
|
|
|
//Allocates memory and copies command
|
|
*command = (char*)(malloc((strlen(token) + 1) * sizeof(char)));
|
|
strcpy(*command, trimWhitespaces(token));
|
|
(*command)[strlen(*command)] = '\0';
|
|
|
|
//Allocates memory and copies first argument (command)
|
|
*argv = (char**)(malloc((*argc) * sizeof(char*)));
|
|
(*argv)[(*argc) - 1] = (char*)(malloc((strlen(*command) + 1) * sizeof(char)));
|
|
strcpy((*argv)[(*argc) - 1], *command);
|
|
(*argv)[(*argc) - 1][strlen((*argv)[(*argc) - 1])] = '\0';
|
|
|
|
//Parses the rest of the arguments
|
|
token = strtok(NULL, " ");
|
|
while (token != NULL){
|
|
++(*argc);
|
|
*argv = (char**)(realloc(*argv, (*argc) * sizeof(char*)));
|
|
|
|
//Checks if argument is wrapped inside " or ' characters
|
|
if ( (token[0] == '"' && token[strlen(token) - 1] == '"' ) ||
|
|
(token[0] == '\'' && token[strlen(token) - 1] == '\'' ) ){
|
|
//Deletes wrapper characters
|
|
token++;
|
|
token[strlen(token) - 1] = '\0';
|
|
|
|
(*argv)[(*argc) - 1] = (char*)(malloc((strlen(token)) * sizeof(char)));
|
|
strcpy((*argv)[(*argc) - 1], trimWhitespaces(token));
|
|
} else {
|
|
(*argv)[(*argc) - 1] = (char*)(malloc((strlen(token) + 1) * sizeof(char)));
|
|
strcpy((*argv)[(*argc) - 1], trimWhitespaces(token));
|
|
(*argv)[(*argc) - 1][strlen((*argv)[(*argc) - 1])] = '\0';
|
|
}
|
|
|
|
token = strtok(NULL, " ");
|
|
}
|
|
|
|
//NULL-terminates arguments array
|
|
++(*argc);
|
|
*argv = (char**)(realloc(*argv, (*argc) * sizeof(char*)));
|
|
(*argv)[(*argc) - 1] = NULL;
|
|
}
|
|
|
|
char *trimWhitespaces(char *string){
|
|
char *end;
|
|
|
|
//Trims leading spaces
|
|
while (isspace( (unsigned char) *string )){
|
|
string++;
|
|
}
|
|
|
|
if(*string == 0){ //String was all spaces
|
|
return string;
|
|
}
|
|
|
|
//Trims trailing spaces
|
|
end = string + strlen(string) - 1;
|
|
while (end > string && isspace( (unsigned char) *end )){
|
|
end--;
|
|
}
|
|
|
|
//Writes new null terminator
|
|
*(end+1) = 0;
|
|
|
|
return string;
|
|
}
|