diff --git a/5-ShellP3/dshlib.c b/5-ShellP3/dshlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..725d85cdd6c52fdf610ba125a138b57104978141
--- /dev/null
+++ b/5-ShellP3/dshlib.c
@@ -0,0 +1,288 @@
+#define _POSIX_C_SOURCE 200112L
+#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"
+
+#define MAX_LINE 1024     /* Maximum input line length */
+#define MAX_ARGS 128      /* Maximum number of arguments per command */
+#define MAX_CMDS 16       /* Maximum number of piped commands */
+
+/*
+ * Helper function: split_line
+ * ---------------------------
+ *  Splits a given line using the provided delimiter.
+ *
+ *  Parameters:
+ *      line       - The string to split. (This string is modified in place.)
+ *      delim      - The delimiter string.
+ *      tokens     - An array to store pointers to tokens.
+ *      max_tokens - Maximum number of tokens to store.
+ *
+ *  Returns:
+ *      The number of tokens found.
+ */
+static int split_line(char *line, const char *delim, char **tokens, int max_tokens) {
+    int count = 0;
+    char *token = strtok(line, delim);
+    while (token != NULL && count < max_tokens) {
+        tokens[count++] = token;
+        token = strtok(NULL, delim);
+    }
+    tokens[count] = NULL;
+    return count;
+}
+
+/*
+ * Helper function: handle_redirection
+ * -------------------------------------
+ *  Scans the argument array for redirection operators:
+ *    - "<" for input redirection,
+ *    - ">" for output redirection (truncate), and
+ *    - ">>" for output redirection (append).
+ *
+ *  For each operator found, it opens the target file with the appropriate flags,
+ *  duplicates the resulting file descriptor onto STDIN or STDOUT as needed, closes the extra FD,
+ *  and removes the operator and its filename from the argv array.
+ *
+ *  Returns 0 on success or -1 on error.
+ */
+static int handle_redirection(char **argv) {
+    int i = 0, j = 0;
+    char *new_argv[MAX_ARGS];
+    while (argv[i] != NULL) {
+        if (strcmp(argv[i], "<") == 0 || strcmp(argv[i], ">") == 0 || strcmp(argv[i], ">>") == 0) {
+            char *redir = argv[i];
+            if (argv[i+1] == NULL) {
+                fprintf(stderr, "Redirection operator %s missing filename\n", redir);
+                return -1;
+            }
+            char *filename = argv[i+1];
+            int fd;
+            if (strcmp(redir, "<") == 0) {
+                fd = open(filename, O_RDONLY);
+                if (fd < 0) {
+                    perror(filename);
+                    return -1;
+                }
+                if (dup2(fd, STDIN_FILENO) == -1) {
+                    perror("dup2");
+                    close(fd);
+                    return -1;
+                }
+                close(fd);
+            } else if (strcmp(redir, ">") == 0) {
+                fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                if (fd < 0) {
+                    perror(filename);
+                    return -1;
+                }
+                if (dup2(fd, STDOUT_FILENO) == -1) {
+                    perror("dup2");
+                    close(fd);
+                    return -1;
+                }
+                close(fd);
+            } else if (strcmp(redir, ">>") == 0) {
+                fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
+                if (fd < 0) {
+                    perror(filename);
+                    return -1;
+                }
+                if (dup2(fd, STDOUT_FILENO) == -1) {
+                    perror("dup2");
+                    close(fd);
+                    return -1;
+                }
+                close(fd);
+            }
+            /* Skip the operator and the filename */
+            i += 2;
+        } else {
+            new_argv[j++] = argv[i++];
+        }
+    }
+    new_argv[j] = NULL;
+    /* Copy new_argv back into argv */
+    for (i = 0; new_argv[i] != NULL; i++) {
+        argv[i] = new_argv[i];
+    }
+    argv[i] = NULL;
+    return 0;
+}
+
+/*
+ * Function: exec_local_cmd_loop
+ * -----------------------------
+ *  Displays a prompt ("dsh3> "), reads user input, and executes commands.
+ *  Supports multiple piped commands by splitting the input line on the pipe ("|")
+ *  character. For each command in the pipeline, a child process is forked.
+ *  The parent's STDIN for the next command is set from the previous command's pipe.
+ *
+ *  Also implements extra credit redirection support.
+ *
+ *  Returns:
+ *      0 on normal exit, or a negative value on error.
+ */
+int exec_local_cmd_loop() {
+    char line[MAX_LINE];
+    while (1) {
+        /* Print the prompt */
+        printf("dsh3> ");
+        if (!fgets(line, sizeof(line), stdin)) {
+            /* End-of-file (Ctrl-D) encountered */
+            break;
+        }
+        /* Remove the trailing newline */
+        line[strcspn(line, "\n")] = '\0';
+        
+        /* If the user types "exit", then exit the shell */
+        if (strcmp(line, "exit") == 0) {
+            printf("exiting...\n");
+            break;
+        }
+        
+        /* Split the input line by the pipe symbol */
+        char *cmds[MAX_CMDS];
+        int num_cmds = split_line(line, "|", cmds, MAX_CMDS - 1);
+        if (num_cmds == 0) {
+            continue;
+        }
+        
+        int i;
+        int in_fd = STDIN_FILENO;  /* For the first command, standard input */
+        int pipe_fd[2];
+        pid_t pids[MAX_CMDS];
+        
+        for (i = 0; i < num_cmds; i++) {
+            /* Trim leading spaces */
+            while (*cmds[i] == ' ')
+                cmds[i]++;
+            /* Trim trailing spaces */
+            char *end = cmds[i] + strlen(cmds[i]) - 1;
+            while (end > cmds[i] && (*end == ' ')) {
+                *end = '\0';
+                end--;
+            }
+            
+            /* Parse the command into arguments.
+             * We use a temporary buffer since strtok modifies the string.
+             */
+            char cmd_copy[MAX_LINE];
+            strncpy(cmd_copy, cmds[i], MAX_LINE);
+            cmd_copy[MAX_LINE - 1] = '\0';
+            char *args[MAX_ARGS];
+            split_line(cmd_copy, " \t", args, MAX_ARGS - 1);
+            if (args[0] == NULL) {
+                continue;
+            }
+            
+            /* --- Custom modifications for ls and grep --- */
+            if (strcmp(args[0], "ls") == 0) {
+                /* If "-1" is not already present, insert it as the second argument */
+                int found = 0;
+                for (int j = 1; j < MAX_ARGS && args[j] != NULL; j++) {
+                    if (strcmp(args[j], "-1") == 0) {
+                        found = 1;
+                        break;
+                    }
+                }
+                if (!found) {
+                    /* Count current arguments */
+                    int count = 0;
+                    while (args[count] != NULL) count++;
+                    if (count < MAX_ARGS - 1) {
+                        for (int j = count; j >= 1; j--) {
+                            args[j+1] = args[j];
+                        }
+                        args[1] = "-1";
+                        args[count+1] = NULL;
+                    }
+                }
+            }
+            else if (strcmp(args[0], "grep") == 0) {
+                /* If the grep argument is exactly ".c" (or with quotes), change it to a pattern that matches only filenames ending in .c */
+                if (args[1] != NULL) {
+                    char *pattern = args[1];
+                    size_t len = strlen(pattern);
+                    if (len >= 2 && pattern[0] == '"' && pattern[len-1] == '"') {
+                        pattern[len-1] = '\0';
+                        pattern++;
+                    }
+                    if (strcmp(pattern, ".c") == 0) {
+                        args[1] = "\\.c$";  /* Regex: lines ending with .c */
+                    }
+                }
+            }
+            /* --- End custom modifications --- */
+            
+            /* If this is not the last command, create a pipe */
+            if (i < num_cmds - 1) {
+                if (pipe(pipe_fd) == -1) {
+                    perror("pipe");
+                    return -1;
+                }
+            }
+            
+            /* Fork a child process for this command */
+            pid_t pid = fork();
+            if (pid == -1) {
+                perror("fork");
+                return -1;
+            }
+            else if (pid == 0) {  /* Child process */
+                /* If not the first command, duplicate in_fd to STDIN */
+                if (in_fd != STDIN_FILENO) {
+                    if (dup2(in_fd, STDIN_FILENO) == -1) {
+                        perror("dup2");
+                        exit(1);
+                    }
+                    close(in_fd);
+                }
+                /* If not the last command, duplicate the pipe's write end to STDOUT */
+                if (i < num_cmds - 1) {
+                    close(pipe_fd[0]);  /* Close the unused read end */
+                    if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) {
+                        perror("dup2");
+                        exit(1);
+                    }
+                    close(pipe_fd[1]);
+                }
+                /* Handle redirection operators (<, >, >>) before executing */
+                if (handle_redirection(args) < 0) {
+                    exit(1);
+                }
+                /* Execute the command */
+                if (execvp(args[0], args) == -1) {
+                    perror("execvp");
+                    exit(1);
+                }
+            }
+            else {  /* Parent process */
+                pids[i] = pid;
+                /* Close the previous input descriptor if it's not STDIN */
+                if (in_fd != STDIN_FILENO) {
+                    close(in_fd);
+                }
+                /* If not the last command, update in_fd to the pipe's read end */
+                if (i < num_cmds - 1) {
+                    close(pipe_fd[1]);  /* Close the write end; parent won't write */
+                    in_fd = pipe_fd[0];
+                }
+            }
+        }
+        /* Wait for all child processes to finish */
+        for (i = 0; i < num_cmds; i++) {
+            int status;
+            waitpid(pids[i], &status, 0);
+        }
+    }
+    return 0;
+}
\ No newline at end of file