Select Git revision
student_tests.sh
-
Ziheng Chen authoredZiheng Chen authored
dshlib.c 11.38 KiB
#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;
}