From 65eec2b982e055a7b0cef577bdada56b8cc402cd Mon Sep 17 00:00:00 2001
From: vht24 <vht24@tux1.cci.drexel.edu>
Date: Wed, 5 Mar 2025 16:27:44 -0500
Subject: [PATCH] Assignment 5

---
 5-ShellP3/bats/assignment_tests.sh |  36 +++
 5-ShellP3/bats/student_tests.sh    | 190 +++++++++++++++
 5-ShellP3/dragon.c                 |  27 +++
 5-ShellP3/dragon_data.h            |   7 +
 5-ShellP3/dsh                      | Bin 0 -> 30480 bytes
 5-ShellP3/dsh_cli.c                |  13 +
 5-ShellP3/dshlib.c                 | 371 +++++++++++++++++++++++++++++
 5-ShellP3/dshlib.h                 |  93 ++++++++
 5-ShellP3/makefile                 |  32 +++
 5-ShellP3/questions.md             |  30 +++
 5-ShellP3/readme.md                | 135 +++++++++++
 11 files changed, 934 insertions(+)
 create mode 100755 5-ShellP3/bats/assignment_tests.sh
 create mode 100755 5-ShellP3/bats/student_tests.sh
 create mode 100644 5-ShellP3/dragon.c
 create mode 100644 5-ShellP3/dragon_data.h
 create mode 100755 5-ShellP3/dsh
 create mode 100644 5-ShellP3/dsh_cli.c
 create mode 100644 5-ShellP3/dshlib.c
 create mode 100644 5-ShellP3/dshlib.h
 create mode 100644 5-ShellP3/makefile
 create mode 100644 5-ShellP3/questions.md
 create mode 100644 5-ShellP3/readme.md

diff --git a/5-ShellP3/bats/assignment_tests.sh b/5-ShellP3/bats/assignment_tests.sh
new file mode 100755
index 0000000..ac34560
--- /dev/null
+++ b/5-ShellP3/bats/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 ]
+}
diff --git a/5-ShellP3/bats/student_tests.sh b/5-ShellP3/bats/student_tests.sh
new file mode 100755
index 0000000..7c98b3f
--- /dev/null
+++ b/5-ShellP3/bats/student_tests.sh
@@ -0,0 +1,190 @@
+#!/usr/bin/env bats
+
+# File: student_tests.sh
+# 
+# Create your unit tests suit in this file
+
+@test "Example: check ls runs without errors" {
+    run ./dsh <<EOF                
+ls
+EOF
+
+    # Assertions
+    [ "$status" -eq 0 ]
+}
+
+@test "Clear Command" {
+    run "./dsh" <<EOF
+clear
+EOF
+
+    stripped_output=$(echo "$output" | sed 's/\x1B\[[0-9;]*[a-zA-Z]//g' | tr -d '[:space:]')
+    expected_output="dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "cd" {
+    run "./dsh" <<EOF
+cd
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "exit" {
+    run "./dsh" <<EOF
+exit
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="dsh3>cmdloopreturned-7"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "Pipes - ps aux | grep dsh" {
+    run "./dsh" <<EOF
+ps aux | grep dsh
+EOF
+
+    [ "$status" -eq 0 ]
+}
+
+@test "Simple Echo Command" {
+    run "./dsh" <<EOF
+echo hello world
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="helloworlddsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "Invalid Command Handling" {
+    run "./dsh" <<EOF
+invalidcmd
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="execvp:Nosuchfileordirectorydsh3>dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "output - echo > tempfile" {
+    run "./dsh" <<EOF
+echo uhh > tempfile
+cat tempfile
+rm tempfile
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="uhhdsh3>dsh3>dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "Append - echo >> tempfile" {
+    run "./dsh" <<EOF
+echo uhh > tempfile
+echo "uuuu" >> tempfile
+cat tempfile
+rm tempfile
+EOF
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="uuuuuhhdsh3>dsh3>dsh3>dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "Input - cat < tempfile" {
+    echo "oowwoo" > tempfile
+    run "./dsh" <<EOF
+cat < tempfile
+rm tempfile
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="oowwoodsh3>dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "Redirection with Pipeline - cat < testfile.txt | grep o" {
+    echo "oowwoo" > testfile.txt
+    run "./dsh" <<EOF
+cat < testfile.txt | grep o
+rm testfile.txt
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="oowwoodsh3>dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
+
+@test "missing File for <" {
+    run "./dsh" <<EOF
+cat < not_a_real_file.file
+EOF
+
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+    expected_output="openinputfile:Nosuchfileordirectorydsh3>dsh3>dsh3>cmdloopreturned0"
+
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    [ "$stripped_output" = "$expected_output" ]
+    [ "$status" -eq 0 ]
+}
diff --git a/5-ShellP3/dragon.c b/5-ShellP3/dragon.c
new file mode 100644
index 0000000..255710f
--- /dev/null
+++ b/5-ShellP3/dragon.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+#include "dragon_data.h"  // Include the embedded binary data
+
+#define DRAGON_BUFFER_SIZE 100000
+
+// EXTRA CREDIT - print the Drexel dragon from embedded binary
+extern void print_dragon() {
+    unsigned char *decompressed_data = (unsigned char *)malloc(DRAGON_BUFFER_SIZE);
+    if (!decompressed_data) {
+        fprintf(stderr, "Memory allocation failed!\n");
+        return;
+    }
+
+    uLongf decompressed_size = DRAGON_BUFFER_SIZE;
+    
+    // Ensure correct decompression
+    if (uncompress(decompressed_data, &decompressed_size, DRAGON_BIN, DRAGON_BIN_SIZE) != Z_OK) {
+        fprintf(stderr, "Decompression fail\n");
+        free(decompressed_data);
+        return;
+    }
+
+    printf("%s", decompressed_data);
+    free(decompressed_data);
+}
\ No newline at end of file
diff --git a/5-ShellP3/dragon_data.h b/5-ShellP3/dragon_data.h
new file mode 100644
index 0000000..5888b1c
--- /dev/null
+++ b/5-ShellP3/dragon_data.h
@@ -0,0 +1,7 @@
+#ifndef DRAGON_DATA_H
+#define DRAGON_DATA_H
+
+const unsigned char DRAGON_BIN[] = { 0x78, 0x9c, 0xb5, 0xd6, 0x41, 0xa2, 0x83, 0x20, 0x0c, 0x04, 0xd0, 0x3d, 0xa7, 0x60, 0x93, 0x7b, 0xe4, 0xfe, 0xa7, 0xea, 0xaf, 0x4a, 0x0a, 0xc9, 0x4c, 0x82, 0xad, 0x9f, 0x45, 0x6b, 0x21, 0xf8, 0x48, 0x44, 0x6d, 0xeb, 0x0f, 0x35, 0x95, 0xbf, 0x46, 0xc6, 0xda, 0x23, 0x82, 0x48, 0x42, 0x3c, 0x84, 0x14, 0xc6, 0x03, 0x88, 0x5c, 0xc6, 0x8c, 0xe8, 0xc3, 0x88, 0x58, 0x73, 0x3d, 0x5f, 0x20, 0x36, 0xf3, 0xfd, 0xa9, 0xbe, 0x5b, 0xac, 0x77, 0x84, 0x2d, 0xfb, 0x60, 0x03, 0x11, 0xd8, 0xb2, 0x31, 0x9f, 0x4a, 0x89, 0xa4, 0x27, 0x59, 0x7e, 0x52, 0xa3, 0x44, 0xb2, 0xa5, 0x86, 0xf3, 0xda, 0xb9, 0x57, 0xa3, 0x40, 0x32, 0x41, 0xcf, 0xba, 0xe3, 0xe5, 0xdf, 0x40, 0xd2, 0x34, 0x58, 0x2a, 0x12, 0xef, 0x98, 0xdf, 0x10, 0xae, 0xff, 0x27, 0x72, 0x55, 0x50, 0x21, 0x62, 0x41, 0x09, 0x51, 0xd3, 0x63, 0x1c, 0x96, 0x0b, 0x67, 0x6a, 0xeb, 0x0a, 0x15, 0x98, 0x7b, 0x63, 0x88, 0x22, 0x64, 0x09, 0xa2, 0x2b, 0xde, 0x29, 0x23, 0x2b, 0x7b, 0xeb, 0x3e, 0x64, 0x4e, 0x85, 0x9d, 0x60, 0x1e, 0x50, 0x16, 0x34, 0x21, 0x9f, 0x60, 0x3f, 0x24, 0x5c, 0x01, 0x77, 0x48, 0xc6, 0x18, 0x12, 0x8c, 0xe4, 0x9a, 0x24, 0x08, 0x74, 0x06, 0x12, 0x8d, 0x50, 0x7a, 0x3e, 0x52, 0x41, 0x8d, 0x0d, 0xa4, 0x4a, 0x89, 0xcc, 0x4f, 0xe9, 0xf4, 0x9a, 0xc4, 0xb2, 0x40, 0x44, 0xcf, 0x33, 0x21, 0x65, 0x1c, 0xde, 0xc9, 0x84, 0xec, 0x6e, 0x96, 0xdc, 0xa7, 0x6b, 0xca, 0x04, 0x6d, 0x30, 0xd7, 0x14, 0xf6, 0xa7, 0x15, 0x7c, 0x8f, 0xb4, 0xee, 0x82, 0x57, 0xc4, 0xcd, 0x55, 0xac, 0xa7, 0x57, 0xe9, 0x40, 0xdc, 0x83, 0x61, 0xce, 0x06, 0xad, 0x6b, 0xfb, 0xca, 0x5b, 0x1e, 0x72, 0x3c, 0xbb, 0x3c, 0x7c, 0xdc, 0xf5, 0xd7, 0x05, 0xc5, 0xc6, 0xa6, 0x72, 0x05, 0x82, 0x07, 0xe4, 0xa0, 0xd8, 0x2c, 0xbc, 0xf1, 0x18, 0x20, 0x86, 0xec, 0x66, 0x5f, 0xec, 0x3d, 0x10, 0x79, 0x1e, 0xb6, 0xbd, 0x75, 0x45, 0x22, 0x65, 0x5c, 0x5c, 0x2b, 0x27, 0x50, 0x82, 0xcc, 0x02, 0x7f, 0x21, 0x5b, 0x3e, 0x23, 0x15, 0xd0, 0xa4, 0xb9, 0x28, 0x10, 0x21, 0xce, 0x34, 0xac, 0x12, 0x5f, 0x7b, 0xb0, 0xd6, 0x7d, 0x79, 0xfd, 0x81, 0x3f, 0x12, 0xc4, 0xc8, 0xf3, 0x02, 0x93, 0x68, 0x26, 0x7b, 0x1b, 0x0d, 0x29, 0xd9, 0xeb, 0xfa, 0x06, 0xd2, 0xed, 0x71, 0x5b, 0x21, 0xc7, 0xb1, 0x26, 0x08, 0x93, 0xba, 0x7d, 0x61, 0x22, 0x4c, 0x2b, 0x91, 0x5f, 0x9a, 0x33, 0xda, 0x0b, 0x76, 0x93, 0x04, 0x47 };
+const unsigned int DRAGON_BIN_SIZE = 365;
+
+#endif // DRAGON_DATA_H
diff --git a/5-ShellP3/dsh b/5-ShellP3/dsh
new file mode 100755
index 0000000000000000000000000000000000000000..0b49b63fb77ad59ec4ce3a7d3036d244dac8fe3e
GIT binary patch
literal 30480
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

literal 0
HcmV?d00001

diff --git a/5-ShellP3/dsh_cli.c b/5-ShellP3/dsh_cli.c
new file mode 100644
index 0000000..9262cf4
--- /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.c b/5-ShellP3/dshlib.c
new file mode 100644
index 0000000..f2f011c
--- /dev/null
+++ b/5-ShellP3/dshlib.c
@@ -0,0 +1,371 @@
+#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 <errno.h>
+#include "dshlib.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 alloc_cmd_buff(cmd_buff_t *cmd_buff) {
+    cmd_buff->_cmd_buffer = (char *)malloc(SH_CMD_MAX * sizeof(char));
+    if (!cmd_buff->_cmd_buffer) {
+        return ERR_MEMORY;
+    }
+    memset(cmd_buff->_cmd_buffer, 0, SH_CMD_MAX);
+    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) {
+    clear_cmd_buff(cmd_buff);
+
+    int i = 0;
+    char *cmd_start = cmd_line;
+    char *token_start = NULL;
+    bool in_quotes = false;
+
+    while (*cmd_start) {
+        // Detect start of quoted argument
+        if (*cmd_start == '\"' && !in_quotes) {
+            in_quotes = true;
+            token_start = ++cmd_start;
+        }
+        // Detect end of quoted argument
+        else if (*cmd_start == '\"' && in_quotes) {
+            in_quotes = false;
+            *cmd_start = '\0';
+            cmd_buff->argv[i++] = token_start;
+        }
+        // Handle normal argument (non-space outside quotes)
+        else if (!in_quotes && (*cmd_start != ' ' && *cmd_start != '\t')) {
+            token_start = cmd_start;
+            while (*cmd_start != ' ' && *cmd_start != '\t' && *cmd_start != '\0') {
+                cmd_start++;  
+            }
+            if (*cmd_start != '\0') {
+                *cmd_start = '\0';
+                cmd_start++;
+            }
+            cmd_buff->argv[i++] = token_start;
+        } 
+        else {
+            cmd_start++;
+        }
+    }
+
+    cmd_buff->argv[i] = NULL;
+    cmd_buff->argc = i;
+
+    if (i > 0) {
+        return OK;
+    } else {
+        return WARN_NO_CMDS;
+}
+}
+
+Built_In_Cmds match_command(const char *input) {
+    if (strcmp(input, "exit") == 0) {
+        return BI_CMD_EXIT;
+    } else if (strcmp(input, "cd") == 0) {
+        return BI_CMD_CD;
+    } else if (strcmp(input, "dragon") == 0) {
+        return BI_CMD_DRAGON;
+    }
+    return BI_NOT_BI;
+}
+
+Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd) {
+    Built_In_Cmds cmd_type = match_command(cmd->argv[0]);
+    if (cmd_type == BI_CMD_EXIT) {
+        return BI_CMD_EXIT;
+    }
+    if (cmd_type == BI_CMD_CD) {
+        if (cmd->argc > 1) {
+            if (chdir(cmd->argv[1]) != 0) {
+                fprintf(stderr, "cd: %s: No such file or directory\n", cmd->argv[1]);
+                return ERR_EXEC_CMD;
+            }
+        }
+        return BI_EXECUTED;
+    }
+    if (cmd_type == BI_CMD_DRAGON) {
+        print_dragon();
+        return BI_EXECUTED;
+    }
+    return BI_NOT_BI;
+}
+
+int exec_cmd(cmd_buff_t *cmd) {
+    pid_t pid = fork();
+
+    if (pid == -1) {
+        perror("fork");
+        return ERR_EXEC_CMD;
+    } else if (pid == 0) { 
+        int fd_in = -1, fd_out = -1;
+        int new_argc = 0;
+        char *new_argv[CMD_ARGV_MAX]; 
+
+        for (int i = 0; i < cmd->argc; i++) {
+            if (strcmp(cmd->argv[i], "<") == 0) {
+                if (cmd->argv[i + 1] == NULL) {
+                    fprintf(stderr, "error: missing input file for redirection\n");
+                    exit(ERR_EXEC_CMD);
+                }
+                fd_in = open(cmd->argv[i + 1], O_RDONLY);
+                if (fd_in == -1) {
+                    perror("open input file");
+                    exit(ERR_EXEC_CMD);
+                }
+                dup2(fd_in, STDIN_FILENO);
+                close(fd_in);
+                i++; 
+            } 
+            else if (strcmp(cmd->argv[i], ">") == 0) {
+                if (cmd->argv[i + 1] == NULL) {
+                    fprintf(stderr, "error: missing output file for redirection\n");
+                    exit(ERR_EXEC_CMD);
+                }
+                fd_out = open(cmd->argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                if (fd_out == -1) {
+                    perror("open output file");
+                    exit(ERR_EXEC_CMD);
+                }
+                dup2(fd_out, STDOUT_FILENO);
+                close(fd_out);
+                i++; 
+            } 
+            else if (strcmp(cmd->argv[i], ">>") == 0) {
+                if (cmd->argv[i + 1] == NULL) {
+                    fprintf(stderr, "error: missing output file for append\n");
+                    exit(ERR_EXEC_CMD);
+                }
+                fd_out = open(cmd->argv[i + 1], O_WRONLY | O_CREAT | O_APPEND, 0644);
+                if (fd_out == -1) {
+                    perror("open output file");
+                    exit(ERR_EXEC_CMD);
+                }
+                dup2(fd_out, STDOUT_FILENO);
+                close(fd_out);
+                i++;
+            } 
+            else {
+                new_argv[new_argc++] = cmd->argv[i];
+            }
+        }
+
+        new_argv[new_argc] = NULL; // Null-terminate cleaned argument list
+
+        execvp(new_argv[0], new_argv);
+        perror("execvp");
+        exit(ERR_EXEC_CMD);
+    } else { // Parent process
+        int status;
+        waitpid(pid, &status, 0);
+        return (WIFEXITED(status)) ? WEXITSTATUS(status) : ERR_EXEC_CMD;
+    }
+}
+
+int build_cmd_list(char *cmd_line, command_list_t *clist) {
+    clear_cmd_buff((cmd_buff_t *)clist); 
+
+    char *token = strtok(cmd_line, PIPE_STRING);
+    int count = 0;
+
+    while (token != NULL) {
+        if (count >= CMD_MAX) {
+            printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
+            return ERR_TOO_MANY_COMMANDS;
+        }
+        build_cmd_buff(token, &clist->commands[count]);
+        count++;
+        token = strtok(NULL, PIPE_STRING);
+    }
+
+    clist->num = count;
+    return (count > 0) ? OK : WARN_NO_CMDS;
+}
+
+int execute_pipeline(command_list_t *clist) {
+    if (clist->num < 1) return WARN_NO_CMDS;
+
+    int pipes[CMD_MAX - 1][2];
+    pid_t pids[CMD_MAX];
+
+    // Create pipes
+    for (int i = 0; i < clist->num - 1; i++) {
+        if (pipe(pipes[i]) == -1) {
+            perror("pipe");
+            return ERR_EXEC_CMD;
+        }
+    }
+
+    // Process input redirection for the first command before forking
+    int fd_in = -1;
+    int first_cmd_argc = 0;
+    char *first_cmd_argv[CMD_ARGV_MAX];
+
+    for (int j = 0; j < clist->commands[0].argc; j++) {
+        if (strcmp(clist->commands[0].argv[j], "<") == 0) {
+            if (clist->commands[0].argv[j + 1] == NULL) {
+                fprintf(stderr, "error: missing input file\n");
+                return ERR_EXEC_CMD;
+            }
+            fd_in = open(clist->commands[0].argv[j + 1], O_RDONLY);
+            if (fd_in == -1) {
+                perror("open input file");
+                return ERR_EXEC_CMD;
+            }
+            j++; // Skip file name argument
+        } else {
+            first_cmd_argv[first_cmd_argc++] = clist->commands[0].argv[j]; // Copy valid arguments
+        }
+    }
+    first_cmd_argv[first_cmd_argc] = NULL;
+
+    // Fork processes for each command
+    for (int i = 0; i < clist->num; i++) {
+        pids[i] = fork();
+
+        if (pids[i] == -1) {
+            perror("fork");
+            return ERR_EXEC_CMD;
+        }
+
+        if (pids[i] == 0) { // Child process
+            if (i == 0 && fd_in != -1) { 
+                dup2(fd_in, STDIN_FILENO);
+                close(fd_in);
+            }
+
+            if (i > 0) { 
+                dup2(pipes[i - 1][0], STDIN_FILENO);
+            }
+            if (i < clist->num - 1) { 
+                dup2(pipes[i][1], STDOUT_FILENO);
+            }
+
+            // Close all pipes in child
+            for (int j = 0; j < clist->num - 1; j++) {
+                close(pipes[j][0]);
+                close(pipes[j][1]);
+            }
+
+            if (i == 0) {
+                execvp(first_cmd_argv[0], first_cmd_argv);
+            } else {
+                execvp(clist->commands[i].argv[0], clist->commands[i].argv);
+            }
+
+            perror("execvp");
+            exit(ERR_EXEC_CMD);
+        }
+    }
+
+    // Close all pipes in parent
+    for (int i = 0; i < clist->num - 1; i++) {
+        close(pipes[i][0]);
+        close(pipes[i][1]);
+    }
+
+    if (fd_in != -1) close(fd_in);
+
+    int status;
+    for (int i = 0; i < clist->num; i++) {
+        waitpid(pids[i], &status, 0);
+    }
+
+    return OK;
+}
+
+int exec_local_cmd_loop() {
+    char cmd_line[SH_CMD_MAX];
+    command_list_t clist;
+
+    while (1) {
+        printf("%s", SH_PROMPT);
+        if (fgets(cmd_line, sizeof(cmd_line), stdin) == NULL) {
+            printf("\n");
+            break;
+        }
+        cmd_line[strcspn(cmd_line, "\n")] = '\0';
+
+        if (build_cmd_list(cmd_line, &clist) == WARN_NO_CMDS) {
+            printf(CMD_WARN_NO_CMD);
+            continue;
+        }
+
+        // If only one command, execute it normally
+        if (clist.num == 1) {
+            Built_In_Cmds result = exec_built_in_cmd(&clist.commands[0]);
+            if (result == BI_CMD_EXIT) {
+                return OK_EXIT;
+            } else if (result == BI_NOT_BI) {
+                exec_cmd(&clist.commands[0]);
+            }
+        } else {
+            execute_pipeline(&clist); // pipeline
+        }
+    }
+
+    return OK;
+}
diff --git a/5-ShellP3/dshlib.h b/5-ShellP3/dshlib.h
new file mode 100644
index 0000000..90ae15f
--- /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);
+void print_dragon();
+
+//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 0000000..0bb9478
--- /dev/null
+++ b/5-ShellP3/makefile
@@ -0,0 +1,32 @@
+# Compiler settings
+CC = gcc
+CFLAGS = -Wall -Wextra -g
+LDFLAGS = -lz
+
+# 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) $(LDFLAGS)
+
+# Clean up build files
+clean:
+	rm -f $(TARGET)
+
+test:
+	bats $(wildcard ./bats/*.sh)
+
+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/questions.md b/5-ShellP3/questions.md
new file mode 100644
index 0000000..85bad27
--- /dev/null
+++ b/5-ShellP3/questions.md
@@ -0,0 +1,30 @@
+1. Your shell forks multiple child processes when executing piped commands. How does your implementation ensure that all child processes complete before the shell continues accepting user input? What would happen if you forgot to call waitpid() on all child processes?
+
+    > When executing piped commands, my implementation ensures that all child processes complete before the shell continues by using `waitpid()` in a loop for each forked child process. 
+    > - Each command in the pipeline is executed in a separate child process created using `fork()`.
+    > - The parent process waits for each child using `waitpid(pid, &status, 0)`, ensuring that all child processes complete before the shell resumes accepting input.
+    > - `waitpid()` make sure that the shell does not continue execution before all commands in the pipeline are finished.
+    > What happens if I forget `waitpid()`?
+    > - If `waitpid()` is not called for each child, completed child processes still exist in the system and are not ended.
+    > - The shell would return to the prompt while background child processes are still running.
+    > - Resource leaks.
+
+2. The dup2() function is used to redirect input and output file descriptors. Explain why it is necessary to close unused pipe ends after calling dup2(). What could go wrong if you leave pipes open?
+
+    > Closing unused pipe ends is essential to prevent issues such as:
+    > - If a process never closes the unused end, the pipe remain open infinitely.
+    > - If the write end of a pipe remains open after redirection, the process reading from the pipe will never receive EOF, causing it to wait forever.
+
+3. Your shell recognizes built-in commands (cd, exit, dragon). Unlike external commands, built-in commands do not require execvp(). Why is cd implemented as a built-in rather than an external command? What challenges would arise if cd were implemented as an external process?
+
+    > - `cd` modifies the shell's working directory using `chdir()`. Since each child process has its own copy of the shell's environment, running `cd` as an external command would only change the directory for that child process, not the main shell.
+    > - Challenge: The shell would need to track and manually apply directory changes after each `cd` execution.
+
+4. Currently, your shell supports a fixed number of piped commands (CMD_MAX). How would you modify your implementation to allow an arbitrary number of piped commands while still handling memory allocation efficiently? What trade-offs would you need to consider?
+
+    > Dynamic memory allocation would be needed for storing unlimited pipes.
+    > Instead of using a fixed-size array, use `realloc()` to expand memory as new piped commands are parsed.
+    > Trade off:
+    > - Increases memory usage.
+    > - Adds complexity to the shell.
+    > - Exceeding file descriptor limit.
\ No newline at end of file
diff --git a/5-ShellP3/readme.md b/5-ShellP3/readme.md
new file mode 100644
index 0000000..7dc0e47
--- /dev/null
+++ b/5-ShellP3/readme.md
@@ -0,0 +1,135 @@
+# Assignment: Custom Shell Part 3 - Pipes
+
+This week we will build on our `dsh` Drexel Shell by implementing pipes. In the first shell assignment we approached this subject by splitting lines of input by the pipe `|` and printing out each separate command parsed between the pipes. In the second assignment we set the pipe splitting logic aside and implemented _execution_ of single commands.
+
+This week we'll bring these concepts together by implementing pipelined execution of multiple commands!
+
+# Reuse Prior Work!
+
+The `dsh` assignments are meant to be additive. This week you'll need to use code you wrote in the first two shell assignments. Be sure to re-use your existing code, and refactor it to meet the requirements of this assignment.
+
+# Pipes
+
+This week you'll need to use a couple new important syscalls - `pipe()` and `dup2()`.
+
+The `pipe()` command creates a 1-way communication path that is implemented with two file descriptions. Conceptually it looks like this:
+
+```txt
+  process-1-output --write--> pipe[fd1]:pipe[fd0] --read--> process-2-input
+```
+
+How do the pipes get "connected" between the processes? That's where `dup2()` comes in.
+
+> Tip: pipes should be created and connected inside a `fork()`; you've already used `fork/execvp` to implement a single command. This time you'll need to do that in a loop, because each command should get it's own `fork/execvp`. So, you'll need to handle `pipe()` and `dup2()` _inside_ each child's fork.
+
+Think of `dup2()` as sort of patching or redirection. The function signature is:
+
+    `dup2(source_fd, target_fd)`
+
+Remember that each forked child has it's own process space and set of file descriptors, to include `STDIN_FILENO` and `STDOUT_FILENO`. So, inside each child process, you could use dup2 to _replace_ the child's STDIN and STDOUT with pipe file descriptors instead.
+
+**Preventing Descriptor Leaks**
+
+When you use `dup2()`, it _copies_ the source FD onto the target, effectively replacing the target. The source is no longer used, and can be closed. If you don't close the original pipes after copying with `dup2()`, you have introduced a **descriptor leak**; this could cause your program to run out of available file descriptors.
+
+# Multiple Children / Waitpid
+
+Since you must create multiple forks to handle piped commands, be sure to wait on all of them with `waitpid()`, which can be used to wait for a specific process id. _(Hint: you'll need to save the pid of each process you fork)._
+
+# Assignment Details
+
+### Step 1 - Review [./starter/dshlib.h](./starter/dshlib.h)
+
+The file [./starter/dshlib.h](./starter/dshlib.h) contains some useful definitions and types. Review the available resources in this file before you start coding - these are intended to make your work easier and more robust!
+
+### Step 2 - Re-implement Your Main Loop and Parsing Code in exec_local_cmd_loop() to parse multiple commands by pipe [./starter/dshlib.c](./starter/dshlib.c)
+
+This should mostly be a refactoring, copy/paste exercise to merge your solutions from the first two shell assignments. You need to combine your pipe tokenizing solution with loading up `cmd_buff_t` ultimately into a `command_list_t`.
+
+### Step 3 - Implement pipelined execution of the parsed commands [./starter/dshlib.c](./starter/dshlib.c)
+
+The first part of this is a refactoring problem - instead of just one `fork/exec`, you'll need to do that in a loop and keep track of the child pids and child exit statuses.
+
+The second part is implementing pipe logic. The section above named "Pipes" contains everything you really need to know! Also, check out demo code from this week's lecture [5-pipe-handling](https://github.com/drexel-systems/SysProg-Class/tree/main/demos/process-thread/5-pipe-handling). 
+
+
+### Step 4 - Create BATS Tests
+
+You should now be familiar with authoring your own [bash-based BATS unit tests](https://bats-core.readthedocs.io/en/stable/tutorial.html#your-first-test) from the prior assignment.
+
+Same as last week, you have an "assignment-tests.sh" file that we provide and you cannot change, and a "student_tests.sh" file that **you need to add your own tests to**:
+
+- your-workspace-folder/
+  - bats/assignement_tests.sh
+  - bats/student_tests.sh
+
+**This week we expect YOU to come up with the majority of the test cases! If you have trouble, look at the tests we provided from prior weeks**
+
+**Be sure to run `make test` and verify your tests pass before submitting assignments.**
+
+### Step 5 - Answer Questions
+
+Answer the questions located in [./questions.md](./questions.md).
+
+### Sample Run with Sample Output
+The below shows a sample run executing multiple commands and the expected program output:
+
+```bash
+./dsh
+dsh3> ls | grep ".c" 
+dragon.c
+dsh_cli.c
+dshlib.c
+dsh3> exit
+exiting...
+cmd loop returned 0
+```
+
+### Extra Credit: +10
+
+Last week you should have learned about redirection (it was part of the assignment's research question). Once you've implemented pipes for this assignment, redirection is a natural next step.
+
+**Requirements**
+
+- parse `<` and `>` during command buffer creation; you'll need to parse and track these on `cmd_buff_t`
+- modify command execution to use `dup2()` in a similar way as you did for pipes; you can copy the in and out fd's specified by the user on to STDIN or STDOUT of the child's forked process
+
+Example run:
+
+```bash
+./dsh
+dsh3> echo "hello, class" > out.txt
+dsh3> cat out.txt
+hello, class
+```
+
+### Extra Credit++: +5
+
+Extend the first extra credit to implement `>>`. This is the same as `>`, except the target fd is in append mode.
+
+Example run:
+
+```bash
+./dsh
+dsh3> echo "hello, class" > out.txt
+dsh3> cat out.txt
+hello, class
+dsh3> echo "this is line 2" >> out.txt
+dsh3> cat out.txt
+hello, class
+this is line 2
+dsh3> 
+```
+
+#### Grading Rubric
+
+- 50 points: Correct implementation of required functionality
+- 5 points:  Code quality (how easy is your solution to follow)
+- 10 points: Answering the written questions: [questions.md](./questions.md)
+- 10 points: Quality and breadth of BATS unit tests
+- 10 points: [EXTRA CREDIT] handle < and > redirection
+- 5 points: [EXTRA CREDIT++] handle >> append redirection
+
+Total points achievable is 90/75.
+
+
-- 
GitLab