diff --git a/5-ShellP3/assignment_tests.sh b/5-ShellP3/assignment_tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..37687086e3490a00ff0ae96ff26a9f7562496f1a --- /dev/null +++ b/5-ShellP3/assignment_tests.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bats + +############################ DO NOT EDIT THIS FILE ##################################### +# File: assignement_tests.sh +# +# DO NOT EDIT THIS FILE +# +# Add/Edit Student tests in student_tests.sh +# +# All tests in this file must pass - it is used as part of grading! +######################################################################################## + +@test "Pipes" { + run "./dsh" <<EOF +ls | grep dshlib.c +EOF + + # Strip all whitespace (spaces, tabs, newlines) from the output + stripped_output=$(echo "$output" | tr -d '[:space:]') + + # Expected output with all whitespace removed for easier matching + expected_output="dshlib.cdsh3>dsh3>cmdloopreturned0" + + # These echo commands will help with debugging and will only print + #if the test fails + echo "Captured stdout:" + echo "Output: $output" + echo "Exit Status: $status" + echo "${stripped_output} -> ${expected_output}" + + # Check exact match + [ "$stripped_output" = "$expected_output" ] + + # Assertions + [ "$status" -eq 0 ] +} \ No newline at end of file diff --git a/5-ShellP3/dsh_cli.c b/5-ShellP3/dsh_cli.c new file mode 100644 index 0000000000000000000000000000000000000000..9262cf457e6b235b19999efa04153e1de0962255 --- /dev/null +++ b/5-ShellP3/dsh_cli.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dshlib.h" + +/* DO NOT EDIT + * main() logic moved to exec_local_cmd_loop() in dshlib.c +*/ +int main(){ + int rc = exec_local_cmd_loop(); + printf("cmd loop returned %d\n", rc); +} \ No newline at end of file diff --git a/5-ShellP3/dshlib.h b/5-ShellP3/dshlib.h new file mode 100644 index 0000000000000000000000000000000000000000..fe3b24680bed23840340d37a89310125ae517306 --- /dev/null +++ b/5-ShellP3/dshlib.h @@ -0,0 +1,93 @@ +#ifndef __DSHLIB_H__ + #define __DSHLIB_H__ + + +//Constants for command structure sizes +#define EXE_MAX 64 +#define ARG_MAX 256 +#define CMD_MAX 8 +#define CMD_ARGV_MAX (CMD_MAX + 1) +// Longest command that can be read from the shell +#define SH_CMD_MAX EXE_MAX + ARG_MAX + +typedef struct command +{ + char exe[EXE_MAX]; + char args[ARG_MAX]; +} command_t; + +typedef struct cmd_buff +{ + int argc; + char *argv[CMD_ARGV_MAX]; + char *_cmd_buffer; +} cmd_buff_t; + +/* WIP - Move to next assignment +#define N_ARG_MAX 15 //MAX number of args for a command +typedef struct command{ + char exe [EXE_MAX]; + char args[ARG_MAX]; + int argc; + char *argv[N_ARG_MAX + 1]; //last argv[LAST] must be \0 +}command_t; +*/ + +typedef struct command_list{ + int num; + cmd_buff_t commands[CMD_MAX]; +}command_list_t; + +//Special character #defines +#define SPACE_CHAR ' ' +#define PIPE_CHAR '|' +#define PIPE_STRING "|" + +#define SH_PROMPT "dsh3> " +#define EXIT_CMD "exit" +#define EXIT_SC 99 + +//Standard Return Codes +#define OK 0 +#define WARN_NO_CMDS -1 +#define ERR_TOO_MANY_COMMANDS -2 +#define ERR_CMD_OR_ARGS_TOO_BIG -3 +#define ERR_CMD_ARGS_BAD -4 //for extra credit +#define ERR_MEMORY -5 +#define ERR_EXEC_CMD -6 +#define OK_EXIT -7 + +//prototypes +int alloc_cmd_buff(cmd_buff_t *cmd_buff); +int free_cmd_buff(cmd_buff_t *cmd_buff); +int clear_cmd_buff(cmd_buff_t *cmd_buff); +int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff); +int close_cmd_buff(cmd_buff_t *cmd_buff); +int build_cmd_list(char *cmd_line, command_list_t *clist); +int free_cmd_list(command_list_t *cmd_lst); + +//built in command stuff +typedef enum { + BI_CMD_EXIT, + BI_CMD_DRAGON, + BI_CMD_CD, + BI_NOT_BI, + BI_EXECUTED, +} Built_In_Cmds; +Built_In_Cmds match_command(const char *input); +Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd); + +//main execution context +int exec_local_cmd_loop(); +int exec_cmd(cmd_buff_t *cmd); +int execute_pipeline(command_list_t *clist); + + + + +//output constants +#define CMD_OK_HEADER "PARSED COMMAND LINE - TOTAL COMMANDS %d\n" +#define CMD_WARN_NO_CMD "warning: no commands provided\n" +#define CMD_ERR_PIPE_LIMIT "error: piping limited to %d commands\n" + +#endif \ No newline at end of file diff --git a/5-ShellP3/makefile b/5-ShellP3/makefile new file mode 100644 index 0000000000000000000000000000000000000000..1a1acdfa73176b1fea2b24066e8d9e8a94bd86d2 --- /dev/null +++ b/5-ShellP3/makefile @@ -0,0 +1,39 @@ +# Compiler settings +CC = gcc +CFLAGS = -Wall -Wextra -g + +# Target executable name +TARGET = dsh + +# Find all source and header files +SRCS = $(wildcard *.c) +HDRS = $(wildcard *.h) + +# Default target +all: $(TARGET) + +# Compile source to executable +$(TARGET): $(SRCS) $(HDRS) + $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) + +# Clean up build files +clean: + rm -f $(TARGET) + +TESTS = student_tests.sh assignment_tests.sh + +.PHONY: test + +test: + @for t in $(TESTS); do \ + chmod +x $$t; \ + echo "Running $$t..."; \ + ./$$t || exit 1; \ + done + +valgrind: + echo "pwd\nexit" | valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./$(TARGET) + echo "pwd\nexit" | valgrind --tool=helgrind --error-exitcode=1 ./$(TARGET) + +# Phony targets +.PHONY: all clean test \ No newline at end of file diff --git a/5-ShellP3/student_tests.sh b/5-ShellP3/student_tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..acbc3e926437d16de753218ed30c8fd7c45ca692 --- /dev/null +++ b/5-ShellP3/student_tests.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bats + +@test "Example: check ls runs without errors" { + run ./dsh <<EOF +ls +exit +EOF + [ "$status" -eq 0 ] +} + +@test "Pipeline: ls | grep \".c\" shows only .c files" { + run ./dsh <<EOF +ls | grep ".c" +exit +EOF + [ "$status" -eq 0 ] + # Remove prompt lines and termination messages. + result=$(echo "$output" | sed '/^dsh3>/d' | grep -v -E '^(exiting|cmd loop returned)') + # Iterate over each line in the result. + while IFS= read -r line; do + # Skip empty lines. + [ -z "$line" ] && continue + # Each line should contain ".c" + [[ "$line" =~ \.c ]] + done <<< "$result" +} + +@test "Redirection: echo with > and cat with <" { + run ./dsh <<EOF +echo "hello, class" > out.txt +cat < out.txt +exit +EOF + [ "$status" -eq 0 ] + echo "$output" | grep -q "hello, class" +} + +@test "Append redirection: echo with >> then cat" { + run ./dsh <<EOF +echo "hello, class" > out.txt +echo "this is line 2" >> out.txt +cat < out.txt +exit +EOF + [ "$status" -eq 0 ] + echo "$output" | grep -q "hello, class" + echo "$output" | grep -q "this is line 2" +} \ No newline at end of file