Skip to content
Snippets Groups Projects
Select Git revision
  • f883b195fa6d462523b592abd4a41a681e79586d
  • main default
2 results

CONTRIBUTING.md

Blame
  • After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.
    dshlib.c NaN GiB
    #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>   
    
    
    
    
    /**** 
     **** FOR REMOTE SHELL USE YOUR SOLUTION FROM SHELL PART 3 HERE
     **** THE MAIN FUNCTION CALLS THIS ONE AS ITS ENTRY POINT TO
     **** EXECUTE THE SHELL LOCALLY
     ****
     */
    
    /*
     * 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() {
        char input_buffer[SH_CMD_MAX];
        command_list_t clist;
    
        while (1) {
            printf("%s", SH_PROMPT);
            if (fgets(input_buffer, SH_CMD_MAX, stdin) == NULL) {
                printf("\n");
                break;
            }
            input_buffer[strcspn(input_buffer, "\n")] = '\0';
            if (strlen(input_buffer) == 0) continue;
    
            // Parsing command list
            int rc = build_cmd_list(input_buffer, &clist);
            if (rc != OK) {
                if (rc == WARN_NO_CMDS) printf(CMD_WARN_NO_CMD);
                else if (rc == ERR_TOO_MANY_COMMANDS) printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
                continue;
            }
    
            // Check built-in commands
            if (clist.num > 1) {
                for (int i = 0; i < clist.num; i++) {
                    Built_In_Cmds bi = match_command(clist.commands[i].argv[0]);
                    if (bi != BI_NOT_BI) {
                        fprintf(stderr, "error: built-in command in pipeline\n");
                        rc = ERR_CMD_ARGS_BAD;
                        break;
                    }
                }
                if (rc != OK) {
                    free_cmd_list(&clist);
                    continue;
                }
            } else {
                cmd_buff_t *cmd = &clist.commands[0];
                Built_In_Cmds bi = match_command(cmd->argv[0]);
                if (bi != BI_NOT_BI) {
                    exec_built_in_cmd(cmd);
                    free_cmd_list(&clist);
                    continue;
                }
            }
    
            // Execution pipeline
            execute_pipeline(&clist);
            free_cmd_list(&clist);
        }
        return OK;
    }
    
    
    
    /**
     * @brief Executes a single command, handling output redirection.
     */
    int exec_cmd(cmd_buff_t *cmd) {
        pid_t pid = fork();
        if (pid < 0) {
            perror("fork failed");
            return ERR_EXEC_CMD;
        }
    
        if (pid == 0) {  // Child Process
            int fd = -1;
            char *output_file = NULL;
            int append_mode = 0;
            char *argv_clean[CMD_ARGV_MAX];
            int i, j = 0;
    
            // Look for `>` or `>>` in the command arguments
            for (i = 0; i < cmd->argc; i++) {
                if (strcmp(cmd->argv[i], ">") == 0 || strcmp(cmd->argv[i], ">>") == 0) {
                    if (i + 1 >= cmd->argc) {
                        fprintf(stderr, "error: no output file specified\n");
                        exit(ERR_EXEC_CMD);
                    }
                    output_file = cmd->argv[i + 1];
                    append_mode = (strcmp(cmd->argv[i], ">>") == 0);
                    i++;  // Skip the filename in arguments
                } else {
                    argv_clean[j++] = cmd->argv[i];  // Copy valid arguments
                }
            }
            argv_clean[j] = NULL;  // Null-terminate the cleaned-up argument list
    
            // Redirect output to a file if needed
            if (output_file) {
                fd = open(output_file, O_WRONLY | O_CREAT | (append_mode ? O_APPEND : O_TRUNC), 0644);
                if (fd < 0) {
                    perror("Failed to open output file");
                    exit(ERR_EXEC_CMD);
                }
                dup2(fd, STDOUT_FILENO);
                close(fd);
            }
    
            // Execute the actual command
            execvp(argv_clean[0], argv_clean);
            perror("execvp failed");
            exit(ERR_EXEC_CMD);
        } else {
            waitpid(pid, NULL, 0);
            return OK;
        }
    }
    
    
    
    
    
    // Processing pipeline execution
    int execute_pipeline(command_list_t *clist) {
        int num_cmds = clist->num;
        int prev_pipe = -1;
        int fd[2];
        pid_t pids[num_cmds];
    
        for (int i = 0; i < num_cmds; i++) {
            if (i < num_cmds - 1 && pipe(fd) < 0) {
                perror("pipe");
                return ERR_EXEC_CMD;
            }
    
            pids[i] = fork();
            if (pids[i] < 0) {
                perror("fork");
                return ERR_EXEC_CMD;
            }
    
            if (pids[i] == 0) { // Child
                if (i > 0) {
                    dup2(prev_pipe, STDIN_FILENO);
                    close(prev_pipe);
                }
                if (i < num_cmds - 1) {
                    dup2(fd[1], STDOUT_FILENO);
                    close(fd[1]);
                }
                // Close all pipe ends
                if (i > 0) close(prev_pipe);
                if (i < num_cmds-1) close(fd[0]);
    
                execvp(clist->commands[i].argv[0], clist->commands[i].argv);
                perror("execvp failed");
                exit(ERR_EXEC_CMD);
            } else { // Parent
                if (i > 0) close(prev_pipe);
                if (i < num_cmds - 1) {
                    prev_pipe = fd[0];
                    close(fd[1]);
                }
            }
        }
    
        // Wait for all children
        for (int i = 0; i < num_cmds; i++) {
            waitpid(pids[i], NULL, 0);
        }
        return OK;
    }
    
    
    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 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 -1;
        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;
    }
    
    
    // Used to split commands by pipeline
    int build_cmd_list(char *cmd_line, command_list_t *clist) {
        clist->num = 0;
        char *saveptr;
        char *token = strtok_r(cmd_line, "|", &saveptr);
    
        while (token != NULL) {
            if (clist->num >= CMD_MAX) {
                fprintf(stderr, CMD_ERR_PIPE_LIMIT, CMD_MAX);
                return ERR_TOO_MANY_COMMANDS;
            }
    
            // Trim leading/trailing spaces
            while (*token == ' ') token++;
            size_t len = strlen(token);
            while (len > 0 && token[len-1] == ' ') token[--len] = '\0';
    
            cmd_buff_t *cmd = &clist->commands[clist->num];
            if (alloc_cmd_buff(cmd) != OK) return ERR_MEMORY;
    
            int rc = build_cmd_buff(token, cmd);
            if (rc != OK) {
                free_cmd_buff(cmd);
                return rc;
            }
    
            clist->num++;
            token = strtok_r(NULL, "|", &saveptr);
        }
    
        if (clist->num == 0) {
            printf(CMD_WARN_NO_CMD);
            return WARN_NO_CMDS;
        }
        return OK;
    }
    
    // Release command list
    int free_cmd_list(command_list_t *clist) {
        for (int i = 0; i < clist->num; i++) {
            free_cmd_buff(&clist->commands[i]);
        }
        clist->num = 0;
        return OK;
    }