diff --git a/main.c b/main.c index 6de2855..7bf49d9 100644 --- a/main.c +++ b/main.c @@ -8,6 +8,7 @@ ========================================================================================= */ + // Libraries needed for the project #include #include @@ -18,15 +19,10 @@ #include #include #include +#include +#include -/* -The libraries , might need to be installed to use their functions. -If you have to install them, simply do: - -1. sudo apt update -2. sudo apt install libreadline-dev (for Ubuntu / Debian) -*/ - +// Include readline libraries (make sure to install them) #include #include @@ -40,132 +36,164 @@ int xsh_fg(char **args); int xsh_bg(char **args); int xsh_execute(char **args); +// New Function Declarations for additional commands +int xsh_pwd(char **args); +int xsh_clear(char **args); +int xsh_echo(char **args); +int xsh_cat(char **args); +int xsh_touch(char **args); +int xsh_mkdir(char **args); +int xsh_rmdir(char **args); +int xsh_rm(char **args); +int xsh_cp(char **args); +int xsh_mv(char **args); + // List of built-in commands and their corresponding functions char *builtin_str[] = { - "cd", - "help", - "exit", - "history", - "jobs", - "fg", - "bg" + "cd", + "help", + "exit", + "history", + "jobs", + "fg", + "bg", + "pwd", + "clear", + "echo", + "cat", + "touch", + "mkdir", + "rmdir", + "rm", + "cp", + "mv" }; int (*builtin_func[]) (char **) = { - &xsh_cd, - &xsh_help, - &xsh_exit, - &xsh_history, - &xsh_jobs, - &xsh_fg, - &xsh_bg + &xsh_cd, + &xsh_help, + &xsh_exit, + &xsh_history, + &xsh_jobs, + &xsh_fg, + &xsh_bg, + &xsh_pwd, + &xsh_clear, + &xsh_echo, + &xsh_cat, + &xsh_touch, + &xsh_mkdir, + &xsh_rmdir, + &xsh_rm, + &xsh_cp, + &xsh_mv }; int xsh_num_builtins() { - return sizeof(builtin_str) / sizeof(char *); + return sizeof(builtin_str) / sizeof(char *); } // Linked list to manage background jobs struct Job { - pid_t pid; - char command[1024]; - struct Job *next; + pid_t pid; + char command[1024]; + struct Job *next; }; struct Job *job_list = NULL; // Helper function to add a job to the job list void add_job(pid_t pid, char *command) { - struct Job *new_job = malloc(sizeof(struct Job)); - new_job->pid = pid; - strcpy(new_job->command, command); - new_job->next = job_list; - job_list = new_job; + struct Job *new_job = malloc(sizeof(struct Job)); + new_job->pid = pid; + strcpy(new_job->command, command); + new_job->next = job_list; + job_list = new_job; } // Helper function to remove a job from the job list void remove_job(pid_t pid) { - struct Job **current = &job_list; - while (*current != NULL) { - if ((*current)->pid == pid) { - struct Job *temp = *current; - *current = (*current)->next; - free(temp); - return; + struct Job **current = &job_list; + while (*current != NULL) { + if ((*current)->pid == pid) { + struct Job *temp = *current; + *current = (*current)->next; + free(temp); + return; + } + current = &((*current)->next); } - current = &((*current)->next); - } } // Built-in command: show current jobs int xsh_jobs(char **args) { - struct Job *current = job_list; - while (current != NULL) { - printf("[%d] %s\n", current->pid, current->command); - current = current->next; - } - return 1; + struct Job *current = job_list; + while (current != NULL) { + printf("[%d] %s\n", current->pid, current->command); + current = current->next; + } + return 1; } // Built-in command: bring job to foreground int xsh_fg(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "xsh: expected PID for fg command\n"); - return 1; - } - pid_t pid = atoi(args[1]); - int status; - - tcsetpgrp(STDIN_FILENO, pid); // Set foreground process group - waitpid(pid, &status, WUNTRACED); // Wait for process + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected PID for fg command\n"); + return 1; + } + pid_t pid = atoi(args[1]); + int status; - if (WIFEXITED(status) || WIFSIGNALED(status)) { - remove_job(pid); // Remove job if it has finished - } + tcsetpgrp(STDIN_FILENO, pid); // Set foreground process group + waitpid(pid, &status, WUNTRACED); // Wait for process - tcsetpgrp(STDIN_FILENO, getpgrp()); // Restore shell as foreground - return 1; + if (WIFEXITED(status) || WIFSIGNALED(status)) { + remove_job(pid); // Remove job if it has finished + } + + tcsetpgrp(STDIN_FILENO, getpgrp()); // Restore shell as foreground + return 1; } // Built-in command: send job to background int xsh_bg(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "xsh: expected PID for bg command\n"); + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected PID for bg command\n"); + return 1; + } + pid_t pid = atoi(args[1]); + kill(pid, SIGCONT); // Continue the process in the background return 1; - } - pid_t pid = atoi(args[1]); - kill(pid, SIGCONT); // Continue the process in the background - return 1; } // Built-in command: change directory int xsh_cd(char **args) { - if (args[1] == NULL) { - fprintf(stderr, "xsh: expected argument to \"cd\"\n"); - } else { - if (chdir(args[1]) != 0) { - perror("xsh"); + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected argument to \"cd\"\n"); + } else { + if (chdir(args[1]) != 0) { + perror("xsh"); + } } - } - return 1; + return 1; } // Built-in command: print help int xsh_help(char **args) { - int i; - printf("Panagiotis' XSH\n"); - printf("Type program names and arguments, and hit enter.\n"); - printf("The following are built-in:\n"); - - for (i = 0; i < xsh_num_builtins(); i++) { - printf(" %s\n", builtin_str[i]); - } - return 1; + int i; + printf("Panagiotis' XSH\n"); + printf("Type program names and arguments, and hit enter.\n"); + printf("The following are built-in:\n"); + + for (i = 0; i < xsh_num_builtins(); i++) { + printf(" %s\n", builtin_str[i]); + } + return 1; } // Built-in command: exit the shell int xsh_exit(char **args) { - return 0; + return 0; } // Built-in command: show history of commands @@ -179,109 +207,198 @@ int xsh_history(char **args) { return 1; } -// Execute external commands, including background tasks -int xsh_launch(char **args) { - pid_t pid; - int status; - int bg = 0; - - // Check if the last argument is "&" for background execution - int i = 0; - while (args[i] != NULL) i++; - if (strcmp(args[i-1], "&") == 0) { - bg = 1; - args[i-1] = NULL; // Remove "&" from arguments - } - - pid = fork(); - if (pid == 0) { - // Child process - if (execvp(args[0], args) == -1) { - perror("xsh"); - } - exit(EXIT_FAILURE); - } else if (pid < 0) { - // Forking error - perror("xsh"); - } else { - // Parent process - if (bg) { - add_job(pid, args[0]); - printf("[%d] %s\n", pid, args[0]); +// Built-in command: print current working directory +int xsh_pwd(char **args) { + char cwd[1024]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + printf("%s\n", cwd); } else { - waitpid(pid, &status, 0); + perror("xsh"); } - } - return 1; + return 1; } -// Split the input line into arguments (tokens) -char **xsh_split_line(char *line) { - int bufsize = 64, position = 0; - char **tokens = malloc(bufsize * sizeof(char*)); - char *token; +// Built-in command: clear the terminal screen +int xsh_clear(char **args) { + printf("\033[H\033[J"); // ANSI escape code to clear screen + return 1; +} - token = strtok(line, " \t\r\n\a"); - while (token != NULL) { - tokens[position] = token; - position++; +// Built-in command: echo text to terminal +int xsh_echo(char **args) { + int i = 1; // Start from the first argument + while (args[i] != NULL) { + printf("%s ", args[i]); + i++; + } + printf("\n"); + return 1; +} - if (position >= bufsize) { - bufsize += 64; - tokens = realloc(tokens, bufsize * sizeof(char*)); +// Built-in command: concatenate and display file content +int xsh_cat(char **args) { + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected argument to \"cat\"\n"); + return 1; + } + + FILE *file = fopen(args[1], "r"); + if (file == NULL) { + perror("xsh"); + return 1; + } + + char c; + while ((c = fgetc(file)) != EOF) { + putchar(c); } + fclose(file); + return 1; +} - token = strtok(NULL, " \t\r\n\a"); - } - tokens[position] = NULL; - return tokens; +// Built-in command: create an empty file +int xsh_touch(char **args) { + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected argument to \"touch\"\n"); + return 1; + } + + FILE *file = fopen(args[1], "a"); // Open for appending; creates file if it doesn't exist + if (file == NULL) { + perror("xsh"); + return 1; + } + fclose(file); + return 1; } -// Execute built-in or external command -int xsh_execute(char **args) { - int i; - if (args[0] == NULL) { - return 1; // Empty command - } - - for (i = 0; i < xsh_num_builtins(); i++) { - if (strcmp(args[0], builtin_str[i]) == 0) { - return (*builtin_func[i])(args); +// Built-in command: create a directory +int xsh_mkdir(char **args) { + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected argument to \"mkdir\"\n"); + return 1; } - } + + if (mkdir(args[1], 0755) != 0) { + perror("xsh"); + } + return 1; +} + +// Built-in command: remove an empty directory +int xsh_rmdir(char **args) { + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected argument to \"rmdir\"\n"); + return 1; + } + + if (rmdir(args[1]) != 0) { + perror("xsh"); + } + return 1; +} - return xsh_launch(args); +// Built-in command: remove a file +int xsh_rm(char **args) { + if (args[1] == NULL) { + fprintf(stderr, "xsh: expected argument to \"rm\"\n"); + return 1; + } + + if (remove(args[1]) != 0) { + perror("xsh"); + } + return 1; } -// Main loop: reads user input, splits into tokens, and executes commands -void xsh_loop(void) { - char *line; - char **args; - int status; +// Built-in command: copy a file +int xsh_cp(char **args) { + if (args[1] == NULL || args[2] == NULL) { + fprintf(stderr, "xsh: expected two arguments to \"cp\"\n"); + return 1; + } + + FILE *src = fopen(args[1], "rb"); + if (src == NULL) { + perror("xsh"); + return 1; + } - using_history(); // Enable history support - - do { - line = readline("xsh> "); // Display prompt and read input - if (!line) break; + FILE *dest = fopen(args[2], "wb"); + if (dest == NULL) { + perror("xsh"); + fclose(src); + return 1; + } - // Add command to history - add_history(line); - - args = xsh_split_line(line); - status = xsh_execute(args); + char buffer[1024]; + size_t bytes; + while ((bytes = fread(buffer, 1, sizeof(buffer), src)) > 0) { + fwrite(buffer, 1, bytes, dest); + } - free(line); - free(args); - } while (status); + fclose(src); + fclose(dest); + return 1; } -// Main entry point -int main(int argc, char **argv) { - // Load config files if any +// Built-in command: move or rename a file +int xsh_mv(char **args) { + if (args[1] == NULL || args[2] == NULL) { + fprintf(stderr, "xsh: expected two arguments to \"mv\"\n"); + return 1; + } + + if (rename(args[1], args[2]) != 0) { + perror("xsh"); + } + return 1; +} + +// Execute the built-in command +int xsh_execute(char **args) { + for (int i = 0; i < xsh_num_builtins(); i++) { + if (strcmp(args[0], builtin_str[i]) == 0) { + return (*builtin_func[i])(args); + } + } + + return 1; // Command not found +} + +// Main loop of the shell +void xsh_loop() { + char *line; + char **args; + int status; + + do { + line = readline("xsh> "); // Prompt for input + if (line && *line) { + add_history(line); // Add line to history + args = malloc(sizeof(char *) * 64); // Allocate space for arguments + char *token = strtok(line, " "); + int position = 0; + + while (token != NULL) { + args[position++] = token; + token = strtok(NULL, " "); + } + args[position] = NULL; // Null-terminate the array of arguments + + status = xsh_execute(args); // Execute the command + free(args); + } + free(line); + } while (status); +} - // Run the main command loop - xsh_loop(); +int main(int argc, char **argv) { + // Initialize history + using_history(); + + xsh_loop(); // Start the shell loop - return EXIT_SUCCESS; + return EXIT_SUCCESS; } +