From 22cc652fc63d46a09e214305a5f163559be7daf4 Mon Sep 17 00:00:00 2001
From: vyvyvyThao <wn73@drexel.edu>
Date: Thu, 20 Feb 2025 22:17:38 -0500
Subject: [PATCH] submit

---
 4-Shell-Part2/bats/assignment_tests.sh | 119 +++++++++++++
 4-Shell-Part2/bats/student_tests.sh    |  14 ++
 4-Shell-Part2/dragon.c                 |   6 +
 4-Shell-Part2/dsh                      | Bin 0 -> 25008 bytes
 4-Shell-Part2/dsh_cli.c                |  13 ++
 4-Shell-Part2/dshlib.c                 | 227 +++++++++++++++++++++++++
 4-Shell-Part2/dshlib.h                 |  79 +++++++++
 4-Shell-Part2/makefile                 |  31 ++++
 4-Shell-Part2/shell_roadmap.md         |  13 ++
 9 files changed, 502 insertions(+)
 create mode 100644 4-Shell-Part2/bats/assignment_tests.sh
 create mode 100644 4-Shell-Part2/bats/student_tests.sh
 create mode 100644 4-Shell-Part2/dragon.c
 create mode 100755 4-Shell-Part2/dsh
 create mode 100644 4-Shell-Part2/dsh_cli.c
 create mode 100644 4-Shell-Part2/dshlib.c
 create mode 100644 4-Shell-Part2/dshlib.h
 create mode 100644 4-Shell-Part2/makefile
 create mode 100644 4-Shell-Part2/shell_roadmap.md

diff --git a/4-Shell-Part2/bats/assignment_tests.sh b/4-Shell-Part2/bats/assignment_tests.sh
new file mode 100644
index 0000000..d1363ce
--- /dev/null
+++ b/4-Shell-Part2/bats/assignment_tests.sh
@@ -0,0 +1,119 @@
+#!/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 "Change directory" {
+    current=$(pwd)
+
+    cd /tmp
+    mkdir -p dsh-test
+
+    run "${current}/dsh" <<EOF                
+cd dsh-test
+pwd
+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="/tmp/dsh-testdsh2>dsh2>dsh2>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 ]
+}
+
+@test "Change directory - no args" {
+    current=$(pwd)
+
+    cd /tmp
+    mkdir -p dsh-test
+
+    run "${current}/dsh" <<EOF                
+cd
+pwd
+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="/tmpdsh2>dsh2>dsh2>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 ]
+}
+
+
+@test "Which which ... which?" {
+    run "./dsh" <<EOF                
+which which
+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="/usr/bin/whichdsh2>dsh2>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" ]
+}
+
+@test "It handles quoted spaces" {
+    run "./dsh" <<EOF                
+   echo " hello     world     " 
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '\t\n\r\f\v')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output=" hello     world     dsh2> dsh2> cmd loop returned 0"
+
+    # 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 "actual:   '${stripped_output}'"
+    echo "expected: '${expected_output}'"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+}
\ No newline at end of file
diff --git a/4-Shell-Part2/bats/student_tests.sh b/4-Shell-Part2/bats/student_tests.sh
new file mode 100644
index 0000000..638bc34
--- /dev/null
+++ b/4-Shell-Part2/bats/student_tests.sh
@@ -0,0 +1,14 @@
+#!/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 ]
+}
diff --git a/4-Shell-Part2/dragon.c b/4-Shell-Part2/dragon.c
new file mode 100644
index 0000000..83d01f2
--- /dev/null
+++ b/4-Shell-Part2/dragon.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+// EXTRA CREDIT - print the drexel dragon from the readme.md
+extern void print_dragon(){
+  // TODO implement 
+}
diff --git a/4-Shell-Part2/dsh b/4-Shell-Part2/dsh
new file mode 100755
index 0000000000000000000000000000000000000000..c1bca0df63cee349d5f10f3c4e9cb78eab820083
GIT binary patch
literal 25008
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
a!t94~!NWFaO2ATABES@b6q*J!E&~9R$s%n4

literal 0
HcmV?d00001

diff --git a/4-Shell-Part2/dsh_cli.c b/4-Shell-Part2/dsh_cli.c
new file mode 100644
index 0000000..9262cf4
--- /dev/null
+++ b/4-Shell-Part2/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/4-Shell-Part2/dshlib.c b/4-Shell-Part2/dshlib.c
new file mode 100644
index 0000000..b68964e
--- /dev/null
+++ b/4-Shell-Part2/dshlib.c
@@ -0,0 +1,227 @@
+#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()
+ */
+
+ void printDragon(){
+    const char dragon[39][103] = {
+"                                                                        @%%%%                       \n",                       
+"                                                                     %%%%%%                         \n",                         
+"                                                                    %%%%%%                          \n",                          
+"                                                                 % %%%%%%%           @              \n",              
+"                                                                %%%%%%%%%%        %%%%%%%           \n",           
+"                                       %%%%%%%  %%%%@         %%%%%%%%%%%%@    %%%%%%  @%%%%        \n",        
+"                                  %%%%%%%%%%%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%%%%%%%%%%%%          \n",          
+"                                %%%%%%%%%%%%%%%%%%%%%%%%%%   %%%%%%%%%%%% %%%%%%%%%%%%%%%           \n",           
+"                               %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%     %%%            \n",            
+"                             %%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%%        %%            \n",            
+"                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%                \n",                
+"                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              \n",              
+"                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@              \n",              
+"      %%%%%%%%@           %%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%      %%                \n",                
+"    %%%%%%%%%%%%%         %%@%%%%%%%%%%%%           %%%%%%%%%%% %%%%%%%%%%%%      @%                \n",                
+"  %%%%%%%%%%   %%%        %%%%%%%%%%%%%%            %%%%%%%%%%%%%%%%%%%%%%%%                        \n",                        
+" %%%%%%%%%       %         %%%%%%%%%%%%%             %%%%%%%%%%%%@%%%%%%%%%%%                       \n",                   
+"%%%%%%%%%@                % %%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%                     \n",              
+"%%%%%%%%@                 %%@%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  \n",         
+"%%%%%%%@                   %%%%%%%%%%%%%%%           %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              \n",    
+"%%%%%%%%%%                  %%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%      %%%%  \n",
+"%%%%%%%%%@                   @%%%%%%%%%%%%%%         %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%%   %%%%%%%%\n",
+"%%%%%%%%%%                  %%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%% %%%%%%%%%\n",
+"%%%%%%%%%@%%@                %%%%%%%%%%%%%%%%@       %%%%%%%%%%%%%%     %%%%%%%%%%%%%%%%%%%%%%%%  %%\n",
+" %%%%%%%%%%                  % %%%%%%%%%%%%%%@        %%%%%%%%%%%%%%   %%%%%%%%%%%%%%%%%%%%%%%%%% %%\n",
+"  %%%%%%%%%%%%  @           %%%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%% \n",
+"   %%%%%%%%%%%%% %%  %  %@ %%%%%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%% \n",
+"    %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%           @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%%%%%% \n",
+"     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              %%%%%%%%%%%%%%%%%%%%%%%%%%%%        %%%   \n",
+"      @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  %%%%%%%%%%%%%%%%%%%%%%%%%               \n",
+"        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%%%%%%  %%%%%%%          \n",
+"           %%%%%%%%%%%%%%%%%%%%%%%%%%                           %%%%%%%%%%%%%%%  @%%%%%%%%%         \n",
+"              %%%%%%%%%%%%%%%%%%%%           @%@%                  @%%%%%%%%%%%%%%%%%%   %%%        \n",
+"                  %%%%%%%%%%%%%%%        %%%%%%%%%%                    %%%%%%%%%%%%%%%    %         \n",
+"                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%            \n",
+"                %%%%%%%%%%%%%%%%%%%%%%%%%%  %%%% %%%                      %%%%%%%%%%  %%%@          \n",
+"                     %%%%%%%%%%%%%%%%%%% %%%%%% %%                          %%%%%%%%%%%%%@          \n",
+"                                                                                 %%%%%%%@           \n"
+    };
+
+
+    for (int i = 0; i < sizeof(dragon) / sizeof(dragon[0]); i++) {
+        for (int j = 0; j < sizeof(dragon[0]) / sizeof(dragon[0][0]); j++) {
+            if (dragon[i][j] == 0) break;
+            putchar((char)dragon[i][j]);
+        }
+    }
+}
+
+int exec_local_cmd_loop()
+{
+    char *cmd_buff;
+    int rc = 0;
+    cmd_buff_t cmd;
+
+    // TODO IMPLEMENT MAIN LOOP
+
+    // TODO IMPLEMENT parsing input to cmd_buff_t *cmd_buff
+    cmd_buff = malloc(SH_CMD_MAX);
+    if (!cmd_buff) return ERR_MEMORY;
+
+    while(1) {
+        printf("%s", SH_PROMPT);
+        
+        if (fgets(cmd_buff, ARG_MAX, stdin) == NULL) {
+            printf("\n");
+            break;
+        }
+
+        // Remove trailing newline
+        cmd_buff[strcspn(cmd_buff, "\n")] = '\0';
+
+        // Skip empty commands
+        if (strlen(cmd_buff) == 0) {
+            printf(CMD_WARN_NO_CMD);
+            continue;
+        }
+
+        // Parse command and arguments
+        char *exe = strtok(cmd_buff, " ");
+        char *args = strtok(NULL, "");
+
+
+        if (exe) {
+            cmd.argc = 0;
+            cmd.argv[cmd.argc++] = exe;
+
+            if (args) {
+                char* lastChar = args + strlen(args) - 1;
+
+                // removing space after the
+                while (*lastChar == ' ') {
+                    *lastChar = '\0';
+                    lastChar--;
+                }
+
+                char *argsNoQuotes = malloc(ARG_MAX);
+                int numQuotes = 0;
+                int argQuoteLength = strlen(args);
+                int argLength = 0;
+                
+                while (*args != '\0') {
+                    if (*args == '\"') {
+                        numQuotes++;
+                        args++;
+                    } else {
+                        *argsNoQuotes = *args;
+                        argLength++;
+                        args++;
+                        argsNoQuotes++;
+                    }
+                }
+    
+                argsNoQuotes -= argLength;
+                cmd.argv[cmd.argc++] = argsNoQuotes;
+            }
+
+            else {
+                cmd.argv[cmd.argc] = NULL;
+            }
+        }
+
+
+        // TODO IMPLEMENT if built-in command, execute builtin logic for exit, cd (extra credit: dragon)
+        // the cd command should chdir to the provided directory; if no directory is provided, do nothing
+
+        if (strcmp(cmd.argv[0], EXIT_CMD) == 0) {
+            break;
+
+        } else if (strcmp(cmd.argv[0], "cd") == 0) {
+            if (cmd.argv[1]) {
+                if (chdir(cmd.argv[1]) != 0) {
+                    perror("cd");
+                    rc = ERR_CMD_ARGS_BAD;
+                }
+            }
+            continue;
+
+        } else if (strcmp(cmd.argv[0], "dragon") == 0) {
+            printDragon();
+            continue;
+        }
+        else if (strcmp(cmd.argv[0], "rc") == 0) {
+            printf("%d\n", rc);
+            continue;
+        }
+        // TODO IMPLEMENT if not built-in command, fork/exec as an external command
+        // for example, if the user input is "ls -l", you would fork/exec the command "ls" with the arg "-l"
+
+        pid_t pid = fork();
+        
+        if (pid < 0) {
+            // printf(CMD_ERR_EXECUTE);
+            return ERR_MEMORY;
+        } else if (pid == 0) {
+            execvp(cmd.argv[0], cmd.argv);
+            // printf(CMD_ERR_EXECUTE);
+            exit(errno);
+        } else {
+            // parent process
+            wait(&rc);
+            if (WIFEXITED(rc)) {
+                rc = WEXITSTATUS(rc);  // Store last return code
+            } else {
+                rc = ERR_EXEC_CMD;
+            }
+            // wait(NULL);
+        }
+    }
+}
diff --git a/4-Shell-Part2/dshlib.h b/4-Shell-Part2/dshlib.h
new file mode 100644
index 0000000..32059cc
--- /dev/null
+++ b/4-Shell-Part2/dshlib.h
@@ -0,0 +1,79 @@
+#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 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;
+*/
+
+
+//Special character #defines
+#define SPACE_CHAR  ' '
+#define PIPE_CHAR   '|'
+#define PIPE_STRING "|"
+
+#define SH_PROMPT "dsh2> "
+#define EXIT_CMD "exit"
+
+//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);
+
+//built in command stuff
+typedef enum {
+    BI_CMD_EXIT,
+    BI_CMD_DRAGON,
+    BI_CMD_CD,
+    BI_NOT_BI,
+    BI_EXECUTED,
+    BI_RC,
+} 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);
+
+
+
+
+//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/4-Shell-Part2/makefile b/4-Shell-Part2/makefile
new file mode 100644
index 0000000..b14b072
--- /dev/null
+++ b/4-Shell-Part2/makefile
@@ -0,0 +1,31 @@
+# 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)
+
+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/4-Shell-Part2/shell_roadmap.md b/4-Shell-Part2/shell_roadmap.md
new file mode 100644
index 0000000..9af1434
--- /dev/null
+++ b/4-Shell-Part2/shell_roadmap.md
@@ -0,0 +1,13 @@
+## Proposed Shell Roadmap
+
+Week 5:  Shell CLI Parser Due
+
+Week 6:  Execute Basic Commands
+
+Week 7:  Add support for pipes (extra-credit for file redirection)
+
+Week 8:  Basic socket assignment
+
+Week 9:  Remote Shell Extension
+
+Week 11: Add support for something like `nsenter` to enter a namespace
\ No newline at end of file
-- 
GitLab