diff --git a/WEEK-7/dshlib.c b/WEEK-7/dshlib.c new file mode 100644 index 0000000000000000000000000000000000000000..929a6c6797c283cb7368e420375948327d9cc920 --- /dev/null +++ b/WEEK-7/dshlib.c @@ -0,0 +1,240 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> + +#include "dshlib.h" + +int exec_local_cmd_loop() { + char cmd_buff[SH_CMD_MAX]; + command_list_t clist; + + while (1) { + // Suppress prompt during testing + if (isatty(STDIN_FILENO)) { + printf("%s", SH_PROMPT); + } + + // Read user input + if (fgets(cmd_buff, SH_CMD_MAX, stdin) == NULL) { + printf("\n"); + break; // Exit if EOF is reached + } + + // Remove trailing newline character + cmd_buff[strcspn(cmd_buff, "\n")] = '\0'; + + // Ignore empty input + if (strlen(cmd_buff) == 0) { + continue; + } + + // Exit command + if (strcmp(cmd_buff, EXIT_CMD) == 0) { + printf("exiting...\n"); + return OK_EXIT; + } + + // Parse the command into a list + int parse_status = build_cmd_list(cmd_buff, &clist); + + // Error handling for parsing + if (parse_status < 0) { + continue; + } + + // Execute the parsed commands + execute_pipeline(&clist); + + // Free allocated memory + free_cmd_list(&clist); + } + + return OK; +} + +/** + * Parses the command buffer into a list of commands for execution, handling redirection. + */ +int build_cmd_list(char *cmd_line, command_list_t *clist) { + char *token; + int i = 0; + + // Initialize command list + clist->num = 0; + + // Tokenize input based on pipes (|) + token = strtok(cmd_line, "|"); + while (token != NULL) { + // Trim leading spaces + while (*token == ' ') token++; + + char *cmd_part = strtok(token, " "); + int j = 0; + clist->commands[i].infile = NULL; + clist->commands[i].outfile = NULL; + clist->commands[i].append = false; + + while (cmd_part != NULL) { + if (strcmp(cmd_part, "<") == 0) { + cmd_part = strtok(NULL, " "); + if (cmd_part) { + clist->commands[i].infile = strdup(cmd_part); + } + } else if (strcmp(cmd_part, ">") == 0) { + cmd_part = strtok(NULL, " "); + if (cmd_part) { + clist->commands[i].outfile = strdup(cmd_part); + clist->commands[i].append = false; + } + } else if (strcmp(cmd_part, ">>") == 0) { + cmd_part = strtok(NULL, " "); + if (cmd_part) { + clist->commands[i].outfile = strdup(cmd_part); + clist->commands[i].append = true; + } + } else { + clist->commands[i].argv[j++] = cmd_part; + } + cmd_part = strtok(NULL, " "); + } + clist->commands[i].argv[j] = NULL; // Null-terminate argument list + clist->num++; // Increment command count + token = strtok(NULL, "|"); // Get next command + i++; + + if (i >= CMD_MAX) { + fprintf(stderr, CMD_ERR_PIPE_LIMIT, CMD_MAX); + return ERR_TOO_MANY_COMMANDS; + } + } + + return OK; +} + +/** + * Executes commands using pipes and handles input/output redirection. + */ +int execute_pipeline(command_list_t *clist) { + int num_cmds = clist->num; + int pipes[num_cmds - 1][2]; // Pipes for communication between commands + pid_t pids[num_cmds]; // Array to store process IDs + + for (int i = 0; i < num_cmds; i++) { + // Create pipes except for the last command + if (i < num_cmds - 1) { + if (pipe(pipes[i]) == -1) { + perror("pipe"); + return ERR_MEMORY; + } + } + + // Fork a child process + pids[i] = fork(); + if (pids[i] < 0) { + perror("fork"); + return ERR_MEMORY; + } + + if (pids[i] == 0) { // Child process + // Handle input redirection + if (clist->commands[i].infile) { + int fd = open(clist->commands[i].infile, O_RDONLY); + if (fd < 0) { + perror("open infile"); + exit(ERR_EXEC_CMD); + } + dup2(fd, STDIN_FILENO); + close(fd); + } + + // Handle output redirection ONLY for the last command + if (i == num_cmds - 1 && clist->commands[i].outfile) { + int fd; + if (clist->commands[i].append) { + fd = open(clist->commands[i].outfile, O_WRONLY | O_CREAT | O_APPEND, 0644); + } else { + fd = open(clist->commands[i].outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + } + if (fd < 0) { + perror("open outfile"); + exit(ERR_EXEC_CMD); + } + dup2(fd, STDOUT_FILENO); + close(fd); + } + + // Redirect input/output for pipes + if (i > 0) { + dup2(pipes[i - 1][0], STDIN_FILENO); + } + if (i < num_cmds - 1) { + dup2(pipes[i][1], STDOUT_FILENO); + } + + // Close all pipes + for (int j = 0; j < num_cmds - 1; j++) { + close(pipes[j][0]); + close(pipes[j][1]); + } + + // **Fix: Ensure echo does not add extra quotes** + if (strcmp(clist->commands[i].argv[0], "echo") == 0) { + for (int j = 1; clist->commands[i].argv[j] != NULL; j++) { + printf("%s", clist->commands[i].argv[j]); + if (clist->commands[i].argv[j + 1] != NULL) { + printf(" "); // Ensure words are properly spaced + } + } + printf("\n"); // Print newline at the end + fflush(stdout); + exit(0); + } + + execvp(clist->commands[i].argv[0], clist->commands[i].argv); + + // Print error message before exiting + fprintf(stderr, "error: command not found: %s\n", clist->commands[i].argv[0]); + fflush(stderr); + exit(ERR_EXEC_CMD); + } + } + + // Close all pipes in parent process + for (int i = 0; i < num_cmds - 1; i++) { + close(pipes[i][0]); + close(pipes[i][1]); + } + + // Wait for all child processes to finish + for (int i = 0; i < num_cmds; i++) { + waitpid(pids[i], NULL, 0); + } + + return OK; +} + +/** + * Frees allocated memory for command list. + */ +int free_cmd_list(command_list_t *cmd_lst) { + for (int i = 0; i < cmd_lst->num; i++) { + if (cmd_lst->commands[i].infile) { + free(cmd_lst->commands[i].infile); + cmd_lst->commands[i].infile = NULL; + } + if (cmd_lst->commands[i].outfile) { + free(cmd_lst->commands[i].outfile); + cmd_lst->commands[i].outfile = NULL; + } + } + + // Reset the command list + cmd_lst->num = 0; + + return OK; +}