diff --git a/Assignment-05/starter/dshlib.c b/Assignment-05/starter/dshlib.c index 0e6738ed007037b9f293051f058931ad8cb98ced..4e869d1b3f8c2ad4b94aeb2121f069b12fe21b27 100644 --- a/Assignment-05/starter/dshlib.c +++ b/Assignment-05/starter/dshlib.c @@ -9,6 +9,174 @@ #include "dshlib.h" +int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { + if (cmd_line == NULL || cmd_buff == NULL) { + return ERR_CMD_OR_ARGS_TOO_BIG; + } + + char *original_line = strdup(cmd_line); + if (original_line == NULL) { + return ERR_MEMORY; + } + + char *trimmed_line = original_line; + while (*trimmed_line == ' ') { + trimmed_line++; + } + + size_t len = strlen(trimmed_line); + while (len > 0 && trimmed_line[len - 1] == ' ') { + trimmed_line[--len] = '\0'; + } + + cmd_buff->argc = 0; + cmd_buff->_cmd_buffer = original_line; + + char *saveptr1; + char *token = strtok_r(trimmed_line, " ", &saveptr1); + + while (token != NULL && cmd_buff->argc < CMD_ARGV_MAX) { + if (token[0] == '"' || token[0] == '\'') { + char *end_quote = strchr(token + 1, token[0]); + if (end_quote != NULL) { + *end_quote = '\0'; + cmd_buff->argv[cmd_buff->argc++] = token + 1; + token = strtok_r(NULL, " ", &saveptr1); + continue; + } + } + cmd_buff->argv[cmd_buff->argc++] = token; + token = strtok_r(NULL, " ", &saveptr1); + } + cmd_buff->argv[cmd_buff->argc] = NULL; + + return OK; +} + + + + +int execute_pipeline(command_list_t *cmd_list) { + int num_commands = cmd_list->num; + int pipefd[2]; + int prev_pipe_read = -1; + pid_t pids[num_commands]; + + for (int i = 0; i < num_commands; i++) { + if (i < num_commands - 1) { + if (pipe(pipefd) == -1) { + perror("pipe"); + return ERR_MEMORY; + } + } + + pids[i] = fork(); + if (pids[i] == -1) { + perror("fork"); + return ERR_MEMORY; + } + + if (pids[i] == 0) { + if (prev_pipe_read != -1) { + dup2(prev_pipe_read, STDIN_FILENO); + close(prev_pipe_read); + } + + if (i < num_commands - 1) { + dup2(pipefd[1], STDOUT_FILENO); + close(pipefd[1]); + close(pipefd[0]); + } + + cmd_buff_t *cmd = &cmd_list->commands[i]; + execvp(cmd->exe, cmd->argv); + perror("execvp"); + exit(EXIT_FAILURE); + } else { + if (prev_pipe_read != -1) { + close(prev_pipe_read); + } + + if (i < num_commands - 1) { + prev_pipe_read = pipefd[0]; + close(pipefd[1]); + } + } + } + + for (int i = 0; i < num_commands; i++) { + waitpid(pids[i], NULL, 0); + } + + return OK; +} + +int parse_pipeline(char *cmd_line, command_list_t *clist) { + if (cmd_line == NULL || clist == NULL) { + return ERR_CMD_OR_ARGS_TOO_BIG; + } + + char *original_line = strdup(cmd_line); + if (original_line == NULL) { + return ERR_MEMORY; + } + + char *trimmed_line = original_line; + while (*trimmed_line == ' ') { + trimmed_line++; + } + size_t len = strlen(trimmed_line); + while (len > 0 && trimmed_line[len - 1] == ' ') { + trimmed_line[--len] = '\0'; + } + + int command_count = 0; + char *saveptr1, *saveptr2; + char *command = strtok_r(trimmed_line, PIPE_STRING, &saveptr1); + + while (command != NULL && command_count < CMD_MAX) { + memset(&clist->commands[command_count], 0, sizeof(command_t)); + + char *token = strtok_r(command, " ", &saveptr2); + if (token != NULL) { + if (strlen(token) >= EXE_MAX) { + free(original_line); + return ERR_CMD_OR_ARGS_TOO_BIG; + } + strcpy(clist->commands[command_count].exe, token); + + int arg_count = 0; + while ((token = strtok_r(NULL, " ", &saveptr2)) != NULL) { + if (arg_count >= ARG_MAX - 1) { + free(original_line); + return ERR_CMD_OR_ARGS_TOO_BIG; + } + if (strlen(token) >= ARG_MAX) { + free(original_line); + return ERR_CMD_OR_ARGS_TOO_BIG; + } + strcpy(clist->commands[command_count].args[arg_count], token); + arg_count++; + } + clist->commands[command_count].args[arg_count] = NULL; + } + + command_count++; + command = strtok_r(NULL, PIPE_STRING, &saveptr1); + } + + if (command != NULL) { + // Too many commands + free(original_line); + return ERR_TOO_MANY_COMMANDS; + } + + clist->num = command_count; + free(original_line); + return OK; +} + + /* * Implement your exec_local_cmd_loop function by building a loop that prompts the * user for input. Use the SH_PROMPT constant from dshlib.h and then @@ -52,8 +220,90 @@ * Standard Library Functions You Might Want To Consider Using (assignment 2+) * fork(), execvp(), exit(), chdir() */ -int exec_local_cmd_loop() -{ - - return OK; + + int exec_local_cmd_loop() { + char cmd_buff[SH_CMD_MAX]; + int rc = OK; + + while (1) { + printf("%s", SH_PROMPT); + + if (fgets(cmd_buff, SH_CMD_MAX, stdin) == NULL) { + printf("\n"); + break; + } + + cmd_buff[strcspn(cmd_buff, "\n")] = '\0'; + + if (strlen(cmd_buff) == 0) { + printf("%s\n", CMD_WARN_NO_CMD); + rc = WARN_NO_CMDS; + continue; + } + + if (strstr(cmd_buff, "|") != NULL) { + command_list_t cmd_list; + if (parse_pipeline(cmd_buff, &cmd_list) != OK) { + fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + rc = ERR_TOO_MANY_COMMANDS; + continue; + } + + if (execute_pipeline(&cmd_list) != OK) { + fprintf(stderr, "%s\n", CMD_ERR_EXECUTE); + rc = ERR_MEMORY; + } + + free_command_list(&cmd_list); + } else { + cmd_buff_t cmd; + if (build_cmd_buff(cmd_buff, &cmd) != OK) { + fprintf(stderr, "%s\n", CMD_ERR_EXECUTE); + rc = ERR_MEMORY; + continue; + } + + if (strcmp(cmd.argv[0], EXIT_CMD) == 0) { + free(cmd._cmd_buffer); + break; + } + + if (strcmp(cmd.argv[0], "cd") == 0) { + if (cmd.argc == 1) { + chdir(getenv("HOME")); + } else if (cmd.argc == 2) { + if (chdir(cmd.argv[1]) != 0) { + perror("cd"); + } + } else { + fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + } + free(cmd._cmd_buffer); + continue; + } + + pid_t pid = fork(); + if (pid < 0) { + perror("fork failed"); + rc = ERR_MEMORY; + } else if (pid == 0) { + execvp(cmd.argv[0], cmd.argv); + perror("execvp failed"); + exit(EXIT_FAILURE); + } else { + int status; + wait(&status); + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + fprintf(stderr, "Command failed with exit code %d\n", WEXITSTATUS(status)); + } + } + } + + free(cmd._cmd_buffer); + } + } + + return rc; }