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

README.md

Blame
  • dshlib.c 11.38 KiB
    #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>   
    
    
    
    /*
     * 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() { // Main loop handling user input and command execution
        char input_buffer[SH_CMD_MAX];
        cmd_buff_t cmd;
        
        while (1) { // Infinite loop to continuously prompt user for input
            printf("%s", SH_PROMPT); // Display shell prompt
            if (fgets(input_buffer, SH_CMD_MAX, stdin) == NULL) { // Read user input
                printf("\n");
                break;
            }
            
            // Remove trailing newline
            input_buffer[strcspn(input_buffer, "\n")] = '\0'; // Remove newline character
            
            // Skip empty input
            if (strlen(input_buffer) == 0) { // Skip empty input
                continue;
            }
            
            // Allocate command buffer
            if (alloc_cmd_buff(&cmd) != OK) {
                printf("Error allocating command buffer.\n");
                continue;
            }
            
            // Build command buffer
            if (build_cmd_buff(input_buffer, &cmd) != OK) {
                printf("Error parsing command.\n");
                free_cmd_buff(&cmd);
                continue;
            }
            
            // Handle built-in commands
            Built_In_Cmds built_in = match_command(cmd.argv[0]); // Identify built-in commands
            if (built_in != BI_NOT_BI) {
                exec_built_in_cmd(&cmd);
                free_cmd_buff(&cmd);
                continue;
            }
            
            // Execute external command
            exec_cmd(&cmd);
            
            // Free command buffer
            free_cmd_buff(&cmd);
        }
        return OK;
    }
    
    int exec_cmd(cmd_buff_t *cmd) {
        pid_t pid = fork(); // Create a child process
        if (pid < 0) {
            perror("fork failed");
            return ERR_EXEC_CMD;
        }
        
        if (pid == 0) { // Child process executes command
            // Replace current process image with the new command using execvp()
            // execvp() looks for the command in PATH and replaces the process if found
            execvp(cmd->argv[0], cmd->argv);
            perror("execvp failed");
            exit(ERR_EXEC_CMD);
        } else { // Parent process waits for child process to complete
            int status;
            waitpid(pid, &status, 0);
            if (WIFEXITED(status)) { // Check if process exited normally
                int exit_status = WEXITSTATUS(status);
               // printf("%d\n", exit_status);
               if (exit_status != 0) {
                return ERR_EXEC_CMD; 
               }
              // continue;
            }
        }
        return OK;
    }
    
    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 BI_RC;
        return BI_NOT_BI;
    }
    
    
    /*
    Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd) {
    
        const char *target_dir;
        // Handle 'exit' command - Terminates the shell
        if (strcmp(cmd->argv[0], "exit") == 0) {
            free_cmd_buff(cmd);
            exit(0);
        }
        
        // Handle 'cd' command - Changes the working directory
        if (strcmp(cmd->argv[0], "cd") == 0) {
           // const char *target_dir;
    
            
            //if (cmd->argc > 1) {
              //  if (access(cmd->argv[1], F_OK) == -1) {
                  //  printf("cd failed: No such directory\n");
               // } else if (access(cmd->argv[1], R_OK | X_OK) == -1) {
                   // printf("cd failed: Permission denied\n");
              //  } else if (chdir(cmd->argv[1]) != 0) {
                  //  printf("cd failed: Unknown error\n");
               // }
          //  } else {
           //     chdir(getenv("/tmp")); // Change to home directory if no argument
         //   }
        //    return BI_EXECUTED;
       // }
        
            if (cmd->argc > 1) {
                target_dir = cmd->argv[1];  // Use provided directory
            } else {
                target_dir = "/temp";  // Default to /tmp
            }
    
            return BI_EXECUTED;
        }
            
    
            // Check if directory exists
            struct stat st;
            if (stat(target_dir, &st) == -1) {
            // Directory does not exist, attempt to create it
                if (mkdir(target_dir, 0777) != 0) {
                    perror("mkdir failed");  // Print error message if mkdir fails
                    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); // Always print new working directory
                }
            }
            
        
            return BI_EXECUTED;
        
    
    
        // Handle 'rc' command - Prints last error code
        if (strcmp(cmd->argv[0], "rc") == 0) {
            printf("%d\n", errno);
            return BI_EXECUTED;
        }
        return BI_NOT_BI;
    }
    */
    
    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 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 > 2) {  // Too many arguments for cd
                fprintf(stderr, "error: cd command accepts at most 1 argument\n");
                return ERR_CMD_ARGS_BAD;
            }
    
            // Use provided directory or default to `/tmp`
            strncpy(target_dir, (cmd->argc > 1) ? cmd->argv[1] : "/tmp", PATH_MAX);
            //target_dir[PATH_MAX - 1] = '\0';  // Ensure null-termination
    
            // Check if directory exists and create if necessary
            struct stat st;
            if (stat(target_dir, &st) == -1) {
                if (mkdir(target_dir, 0777) != 0) {
                    perror("mkdir failed");
                    return BI_EXECUTED;
                }
            }
    
            // Ensure the directory is accessible
            if (access(target_dir, R_OK | X_OK) != 0) {
                perror("cd failed: Permission denied");
                return BI_EXECUTED;
            }
    
            //  Change directory and print current path
            if (chdir(target_dir) == 0) {
                char cwd[PATH_MAX];
                if (getcwd(cwd, sizeof(cwd)) != NULL) {
                    printf("%s\n", cwd);
                } else {
                    perror("getcwd failed");
                }
            } else {
                perror("cd failed");
            }
    
            return BI_EXECUTED;
        }
    
        if (strcmp(cmd->argv[0], "rc") == 0) {
            printf("%d\n", errno);
            return BI_EXECUTED;
        }
    
        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;
    }