diff --git a/w6/dshlib.c b/w6/dshlib.c new file mode 100644 index 0000000000000000000000000000000000000000..25e23e0b100c2104c27eaa5c36dffe0a0bdcc635 --- /dev/null +++ b/w6/dshlib.c @@ -0,0 +1,400 @@ +#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" +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> + + + +/* + * 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 + * use fgets to accept user input. + * + * while(1){ + * printf("%s", SH_PROMPT); + * if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){ + * printf("\n"); + * break; + * } + * //remove the trailing \n from cmd_buff + * cmd_buff[strcspn(cmd_buff,"\n")] = '\0'; + * + * //IMPLEMENT THE REST OF THE REQUIREMENTS + * } + * + * Also, use the constants in the dshlib.h in this code. + * SH_CMD_MAX maximum buffer size for user input + * EXIT_CMD constant that terminates the dsh program + * SH_PROMPT the shell prompt + * OK the command was parsed properly + * WARN_NO_CMDS the user command was empty + * ERR_TOO_MANY_COMMANDS too many pipes used + * ERR_MEMORY dynamic memory management failure + * + * errors returned + * OK No error + * ERR_MEMORY Dynamic memory management failure + * WARN_NO_CMDS No commands parsed + * ERR_TOO_MANY_COMMANDS too many pipes used + * + * console messages + * CMD_WARN_NO_CMD print on WARN_NO_CMDS + * CMD_ERR_PIPE_LIMIT print on ERR_TOO_MANY_COMMANDS + * CMD_ERR_EXECUTE print on execution failure of external command + * + * Standard Library Functions You Might Want To Consider Using (assignment 1+) + * malloc(), free(), strlen(), fgets(), strcspn(), printf() + * + * Standard Library Functions You Might Want To Consider Using (assignment 2+) + * fork(), execvp(), exit(), chdir() + */ + + + int exec_local_cmd_loop() { // Main loop handling user input and command execution + char input_buffer[SH_CMD_MAX]; + cmd_buff_t cmd; + + while (1) { // Infinite loop to continuously prompt user for input + printf("%s", SH_PROMPT); // Display shell prompt + if (fgets(input_buffer, SH_CMD_MAX, stdin) == NULL) { // Read user input + printf("\n"); + break; + } + + // Remove trailing newline + input_buffer[strcspn(input_buffer, "\n")] = '\0'; // Remove newline character + + // Skip empty input + if (strlen(input_buffer) == 0) { // Skip empty input + continue; + } + + // Allocate command buffer + if (alloc_cmd_buff(&cmd) != OK) { + printf("Error allocating command buffer.\n"); + continue; + } + + // Build command buffer + if (build_cmd_buff(input_buffer, &cmd) != OK) { + printf("Error parsing command.\n"); + free_cmd_buff(&cmd); + continue; + } + + // Handle built-in commands + Built_In_Cmds built_in = match_command(cmd.argv[0]); // Identify built-in commands + if (built_in != BI_NOT_BI) { + exec_built_in_cmd(&cmd); + free_cmd_buff(&cmd); + continue; + } + + // Execute external command + exec_cmd(&cmd); + + // Free command buffer + free_cmd_buff(&cmd); + } + return OK; +} + +int exec_cmd(cmd_buff_t *cmd) { + pid_t pid = fork(); // Create a child process + if (pid < 0) { + perror("fork failed"); + return ERR_EXEC_CMD; + } + + if (pid == 0) { // Child process executes command + // Replace current process image with the new command using execvp() + // execvp() looks for the command in PATH and replaces the process if found + execvp(cmd->argv[0], cmd->argv); + perror("execvp failed"); + exit(ERR_EXEC_CMD); + } else { // Parent process waits for child process to complete + int status; + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { // Check if process exited normally + int exit_status = WEXITSTATUS(status); + // printf("%d\n", exit_status); + if (exit_status != 0) { + return ERR_EXEC_CMD; + } + // continue; + } + } + return OK; +} + +Built_In_Cmds match_command(const char *input) { + if (strcmp(input, "exit") == 0) return BI_CMD_EXIT; + if (strcmp(input, "cd") == 0) return BI_CMD_CD; + if (strcmp(input, "rc") == 0) return BI_RC; + return BI_NOT_BI; +} + + +/* +Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd) { + + const char *target_dir; + // Handle 'exit' command - Terminates the shell + if (strcmp(cmd->argv[0], "exit") == 0) { + free_cmd_buff(cmd); + exit(0); + } + + // Handle 'cd' command - Changes the working directory + if (strcmp(cmd->argv[0], "cd") == 0) { + // const char *target_dir; + + + //if (cmd->argc > 1) { + // if (access(cmd->argv[1], F_OK) == -1) { + // printf("cd failed: No such directory\n"); + // } else if (access(cmd->argv[1], R_OK | X_OK) == -1) { + // printf("cd failed: Permission denied\n"); + // } else if (chdir(cmd->argv[1]) != 0) { + // printf("cd failed: Unknown error\n"); + // } + // } else { + // chdir(getenv("/tmp")); // Change to home directory if no argument + // } + // return BI_EXECUTED; + // } + + if (cmd->argc > 1) { + target_dir = cmd->argv[1]; // Use provided directory + } else { + target_dir = "/temp"; // Default to /tmp + } + + return BI_EXECUTED; + } + + + // Check if directory exists + struct stat st; + if (stat(target_dir, &st) == -1) { + // Directory does not exist, attempt to create it + if (mkdir(target_dir, 0777) != 0) { + perror("mkdir failed"); // Print error message if mkdir fails + return BI_EXECUTED; + } + } + + // Attempt to change directory + if (chdir(target_dir) != 0) { + perror("cd failed"); + } else { + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + printf("%s\n", cwd); // Always print new working directory + } + } + + + return BI_EXECUTED; + + + + // Handle 'rc' command - Prints last error code + if (strcmp(cmd->argv[0], "rc") == 0) { + printf("%d\n", errno); + return BI_EXECUTED; + } + return BI_NOT_BI; +} +*/ + +Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd) { + if (strcmp(cmd->argv[0], "exit") == 0) { + free_cmd_buff(cmd); + exit(0); + } + + if (strcmp(cmd->argv[0], "cd") == 0) { + char target_dir[PATH_MAX]; + + if (cmd->argc > 1) { + strncpy(target_dir, cmd->argv[1], PATH_MAX); + } if (cmd->argc > 2) { // Too many arguments for cd + fprintf(stderr, "error: cd command accepts at most 1 argument\n"); + return ERR_CMD_ARGS_BAD; + } + // strncpy(target_dir, "/tmp", PATH_MAX); // Default to /tmp + + + // Ensure string is null-terminated + target_dir[PATH_MAX - 1] = '\0'; + + // Check if directory exists + struct stat st; + if (stat(target_dir, &st) == -1) { + if (mkdir(target_dir, 0777) != 0) { + //perror("mkdir failed"); + return BI_EXECUTED; + } + } + + // Check permissions before `chdir()` + if (access(target_dir, R_OK | X_OK) != 0) { + perror("cd failed"); + return BI_EXECUTED; + } + + // Attempt to change directory + if (chdir(target_dir) != 0) { + perror("cd failed"); + } else { + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + //printf("%s\n", cwd); // Print working directory only once + } else { + perror("getcwd failed"); + } + } + + return BI_EXECUTED; + } + + if (strcmp(cmd->argv[0], "rc") == 0) { + printf("%d\n", errno); + return BI_EXECUTED; + } + + return BI_NOT_BI; +} + + +/* +Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd) { + if (strcmp(cmd->argv[0], "exit") == 0) { + free_cmd_buff(cmd); + exit(0); + } + + if (strcmp(cmd->argv[0], "cd") == 0) { + char target_dir[PATH_MAX]; + + if (cmd->argc > 2) { // Too many arguments for cd + fprintf(stderr, "error: cd command accepts at most 1 argument\n"); + return ERR_CMD_ARGS_BAD; + } + + // Use provided directory or default to `/tmp` + strncpy(target_dir, (cmd->argc > 1) ? cmd->argv[1] : "/tmp", PATH_MAX); + //target_dir[PATH_MAX - 1] = '\0'; // Ensure null-termination + + // Check if directory exists and create if necessary + struct stat st; + if (stat(target_dir, &st) == -1) { + if (mkdir(target_dir, 0777) != 0) { + perror("mkdir failed"); + return BI_EXECUTED; + } + } + + // Ensure the directory is accessible + if (access(target_dir, R_OK | X_OK) != 0) { + perror("cd failed: Permission denied"); + return BI_EXECUTED; + } + + // Change directory and print current path + if (chdir(target_dir) == 0) { + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + printf("%s\n", cwd); + } else { + perror("getcwd failed"); + } + } else { + perror("cd failed"); + } + + return BI_EXECUTED; + } + + if (strcmp(cmd->argv[0], "rc") == 0) { + printf("%d\n", errno); + return BI_EXECUTED; + } + + return BI_NOT_BI; +} + +*/ + + +int alloc_cmd_buff(cmd_buff_t *cmd_buff) { + cmd_buff->_cmd_buffer = (char *)malloc(SH_CMD_MAX); + if (!cmd_buff->_cmd_buffer) return ERR_MEMORY; + memset(cmd_buff, 0, sizeof(cmd_buff_t)); + return OK; +} + +int free_cmd_buff(cmd_buff_t *cmd_buff) { + if (cmd_buff->_cmd_buffer) { + free(cmd_buff->_cmd_buffer); + cmd_buff->_cmd_buffer = NULL; + } + return OK; +} + +int clear_cmd_buff(cmd_buff_t *cmd_buff) { + memset(cmd_buff, 0, sizeof(cmd_buff_t)); + return OK; +} + +int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { + cmd_buff->argc = 0; + char *ptr = cmd_line; + int in_quotes = 0; + char *token_start = NULL; + + while (*ptr) { + while (isspace(*ptr) && !in_quotes) ptr++; // Skip spaces outside quotes + + if (*ptr == '"') { // Detect start of quoted string + in_quotes = !in_quotes; + ptr++; + token_start = ptr; + } else { + token_start = ptr; + } + + while (*ptr && (in_quotes || !isspace(*ptr))) { // Capture argument + if (*ptr == '"') { + in_quotes = !in_quotes; + *ptr = '\0'; // Properly terminate quoted string + } + ptr++; + } + + if (*ptr) { + *ptr = '\0'; // Terminate non-quoted argument + ptr++; + } + + cmd_buff->argv[cmd_buff->argc++] = token_start; + //if (cmd_buff->argc >= CMD_ARGV_MAX - 1) break; + if (cmd_buff->argc > CMD_ARGV_MAX - 1) { + fprintf(stderr, "error: too many arguments\n"); + return ERR_CMD_ARGS_BAD; // Return error -4 + } + } + cmd_buff->argv[cmd_buff->argc] = NULL; + return OK; +} +