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

dshlib.c

Blame
  • dshlib.c 10.73 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 <errno.h>
    #include "dshlib.h"
    
    int numInstanceOf(char *str, const char c) {
        int count = 0;
        while (*str != '\0') {
            if (*str == c) count++;
            str++;
        }
        return count;
    }
    
    void print_dragon(){
        // TODO implement
        FILE *dragon = fopen("dragon.txt", "r");
        if (dragon == NULL) {
                return;
        }
    
        char *s = NULL;
        size_t nbyte;
        ssize_t nchar;
    
        while (1) {
                nchar = getline(&s, &nbyte, dragon);
                if (nchar == -1) { // end of file reached
                break;
        }
        if (nchar == 0) {
        	continue;
        }
        if (s == NULL) { // out of memory
            exit(1);
        }
        if (s[nchar - 1] == '\n') {
            s[nchar - 1] = '\0'; // remove newline
            nchar--; // newline removed
        }
    
        printf("%s\n", s);
        }
        free(s);
        fclose(dragon);
    }
    
    char* trim_whitespace(char *str) {
        int start = 0;
        while (isspace((unsigned char)str[start])) {
            start++;
        }
    
        int end = strlen(str) - 1;
        while (end > start && isspace((unsigned char)str[end])) {
            end--;
        }
    
        int j = 0;
        for (int i = start; i <= end; i++) {
            str[j++] = str[i];
        }
        str[j] = '\0';
    
        return str;
    }
    
    int alloc_cmd_buff(cmd_buff_t *cmd_buff) {
        if (cmd_buff == NULL) return ERR_MEMORY;
        
        cmd_buff->argc = 0;
    
        cmd_buff->argv = (char**)malloc(CMD_ARGV_MAX * sizeof(char *));
        if (cmd_buff->argv == NULL) return ERR_MEMORY;
    
        for (int i = 0; i < CMD_ARGV_MAX; i++) {
            cmd_buff->argv[i] = (char *)malloc(ARG_MAX * sizeof(char));
            if (cmd_buff->argv[i] == NULL) {
                for (int j = 0; j < i; j++) free(cmd_buff->argv[j]);
                free(cmd_buff->argv);
                return ERR_MEMORY;
            }
        }
    
        cmd_buff->_cmd_buffer = (char *)malloc(SH_CMD_MAX * sizeof(char));
        if (cmd_buff->_cmd_buffer == NULL) {
            for (int i = 0; i < CMD_ARGV_MAX; i++) free(cmd_buff->argv[i]);
            free(cmd_buff->argv);
            return ERR_MEMORY;
        }
    
        return OK;
    }
    
    void free_cmd_buff(cmd_buff_t *cmd_buff) {
        if (cmd_buff != NULL) {
    		if (cmd_buff->_cmd_buffer != NULL) {
        		free(cmd_buff->_cmd_buffer);
        		cmd_buff->_cmd_buffer = NULL;
    		}
    
    		if (cmd_buff->argv != NULL) {
        		for (int i = 0; i < CMD_ARGV_MAX; i++) {
        			if (cmd_buff->argv[i] != NULL) {
        				free(cmd_buff->argv[i]);
        				cmd_buff->argv[i] = NULL;
        			}
        		}
        		free(cmd_buff->argv);
        		cmd_buff->argv = NULL;
        	}
        	memset(cmd_buff, 0, sizeof(cmd_buff_t));
        }
    }
    
    void clear_cmd_buff(cmd_buff_t *cmd_buff) {
        if (cmd_buff != NULL) {
        	cmd_buff->argc = 0;
        	if (cmd_buff->_cmd_buffer) memset(cmd_buff->_cmd_buffer, 0, strlen(cmd_buff->_cmd_buffer));
        	for (int i = 0; i < CMD_ARGV_MAX; i++) cmd_buff->argv[i] = NULL;
        }
    }
    
    int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) {
        if ((int)strlen(cmd_line) > SH_CMD_MAX) return ERR_CMD_OR_ARGS_TOO_BIG;
    
        if ((int)strlen(cmd_line) == 0) return WARN_NO_CMDS;
    	
    	if (cmd_buff->_cmd_buffer != NULL) {
    		free(cmd_buff->_cmd_buffer);
    		cmd_buff->_cmd_buffer = strdup(trim_whitespace(cmd_line));
    	} else return ERR_MEMORY;
    
        char *token = cmd_buff->_cmd_buffer;
        bool quotes = false;
        char *p = NULL;
    
        while (*token) {
            if (*token == DOUBLE_QUOTE_CHAR) {
                quotes = !quotes;
                if (quotes) p = token + 1;
                else *token = '\0';
            } else if (!quotes && (*token == SPACE_CHAR || *token == '\t')) {
                *token = '\0';
    
                if (p != NULL) {
                    cmd_buff->argv[cmd_buff->argc++] = p;
                    p = NULL;
                }
            } else if (p == NULL) {
                p = token;
            }
            token++;
        }
    	
    	if (p != NULL) {
    		if (cmd_buff->argc >= CMD_ARGV_MAX - 1) return ERR_CMD_OR_ARGS_TOO_BIG;
        	cmd_buff->argv[cmd_buff->argc++] = p;
    	}
    
        cmd_buff->argv[cmd_buff->argc] = NULL;
        return OK;
    }
    
    int alloc_cmd_list(command_list_t *clist, int rc) {
    	if (clist == NULL) return ERR_MEMORY;
    	clist->num = 0;
    	clist->commands = (cmd_buff_t **)calloc(CMD_MAX, sizeof(cmd_buff_t *));
    	if (clist->commands == NULL) return ERR_MEMORY;
    
    	for (int i = 0; i < CMD_MAX; i++) {
    		cmd_buff_t *cmd = (cmd_buff_t *)malloc(sizeof(cmd_buff_t));
    		if (cmd == NULL) {
    			rc = ERR_MEMORY;
    			break;
    		}
    
    		memset(cmd, 0, sizeof(cmd_buff_t));
    
    		if ((rc = alloc_cmd_buff(cmd)) != OK_EXIT) {
    			free(cmd);
    			break;
            }
            clist->commands[i] = cmd;
        }
    	
        return rc;
    }
    
    int build_cmd_list(char *cmd_line, command_list_t *clist, int rc) {
        if (numInstanceOf(cmd_line, PIPE_CHAR) > CMD_MAX-1) return ERR_TOO_MANY_COMMANDS;
    
        if ((int)strlen(cmd_line) > SH_CMD_MAX) return ERR_CMD_OR_ARGS_TOO_BIG;
    
        char *outer_saveptr = NULL;
        
        char *outer_token = strtok_r(cmd_line, PIPE_STRING, &outer_saveptr);
    
        while (outer_token != NULL) {
            if (clist->num > CMD_MAX) return ERR_TOO_MANY_COMMANDS;
    		
    		cmd_buff_t *cmd = malloc(sizeof(cmd_buff_t));
    		
    		if ((rc = alloc_cmd_buff(cmd)) != OK) {
    			free(cmd);
    			return rc;
    		}
    
            if ((rc = build_cmd_buff(outer_token, cmd)) != OK) {
            	free(cmd);
            	return rc;
            }
    
            clist->commands[clist->num] = cmd;
            clist->num++;
    
            outer_token = strtok_r(NULL, PIPE_STRING, &outer_saveptr);
        }
    
        return OK;
    }
    
    void free_cmd_list(command_list_t *clist) {
    	if (clist != NULL) {
    		if (clist->commands != NULL) {
    			for (int i = 0; i < CMD_MAX; i++) {
    				if (clist->commands[i] != NULL) {
    					free_cmd_buff(clist->commands[i]);
    					free(clist->commands[i]);
    					clist->commands[i] = NULL;
    				}
    			}
    			free(clist->commands);
    			clist->commands = NULL;
    		}
    	}
    }
    
    void clear_cmd_list(command_list_t *clist) {
    	if (clist != NULL) {
    		clist->num = 0;
    		for (int i = 0; i < CMD_MAX; i++) clear_cmd_buff(clist->commands[i]);
    	}
    }
    
    int exec_pipeline(command_list_t *clist) {
    	int pipes[clist->num - 1][2];
        pid_t pids[clist->num];
    
        for (int i = 0; i < clist->num - 1; i++) {
            if (pipe(pipes[i]) == -1) {
                perror("pipe");
                exit(EXIT_FAILURE);
            }
        }
    
    	for (int i = 0; i < clist->num; i++) {
            pids[i] = fork();
            if (pids[i] == -1) {
                perror("fork");
                exit(EXIT_FAILURE);
            }
    
            if (pids[i] == 0) {
                if (i > 0) dup2(pipes[i-1][0], STDIN_FILENO);
    
                if (i < clist->num - 1) dup2(pipes[i][1], STDOUT_FILENO);
    
                for (int j = 0; j < clist->num - 1; j++) {
                    close(pipes[j][0]);
                    close(pipes[j][1]);
                }
    
                execvp(clist->commands[i]->argv[0], clist->commands[i]->argv);
                perror("execvp");
                exit(EXIT_FAILURE);
            }
        }
    
        for (int i = 0; i < clist->num - 1; i++) {
            close(pipes[i][0]);
            close(pipes[i][1]);
        }
    
        for (int i = 0; i < clist->num; i++) waitpid(pids[i], NULL, 0);
    	
    	return OK;
    }
    
    int exec_one_cmd(command_list_t *clist, int rc) {
    	cmd_buff_t *cmd = clist->commands[0];
    	if (strcmp(cmd->argv[0], "dragon") == 0) {
    		print_dragon();
    		return OK;
    	}
    
    	if (strcmp(cmd->argv[0], "rc") == 0) {
    		printf("%d\n", rc);
    		return OK;
    	}
    
    	if (strcmp(cmd->argv[0], EXIT_CMD) == 0) {
    		printf("exiting...\n");
    		return(OK_EXIT);
    	}
    	
    	cmd->argv[cmd->argc] = 0;
    
    	if (strcmp(cmd->argv[0], "cd") == 0) chdir(cmd->argv[1]);
    	else {	
    		int f_result, c_result;
    		f_result = fork();
    
    		if (f_result < 0) perror("fork failed");
    		else if (f_result == 0) {
    			rc = execvp(cmd->argv[0], cmd->argv);
    			perror("execvp failed");
    			exit(EXIT_FAILURE);
    		} else {
    			wait(&c_result);
    		}
    	}
    	return OK;
    }
    
    int exec_cmd(command_list_t *clist, int rc) {
    	if (clist == NULL) return ERR_MEMORY;
    	
    	if (rc == ERR_TOO_MANY_COMMANDS) {
            printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
            return rc;
        }
    
    	char *cmd = clist->commands[0]->argv[0];
        if (!cmd) return ERR_MEMORY;
    	
    	if (clist->num == 1) {
    		if ((rc = exec_one_cmd(clist, rc)) != OK) {
    			if (rc == OK_EXIT) {
    				clear_cmd_list(clist);
    				return rc;
    			} else return ERR_EXEC_CMD;
    		}
    	}
    
    	if (clist->num > 1) {
    		if ((rc = exec_pipeline(clist)) != OK) return ERR_EXEC_CMD;
    	}
    
    	return OK;
    }
    
    /*
     * 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 *cmd_line = (char *)malloc(ARG_MAX * sizeof(char));
       	int rc = OK;
    	command_list_t *clist = (command_list_t *)malloc(sizeof(command_list_t));
    	
    	if ((rc = alloc_cmd_list(clist, rc)) != OK) {
    		free(cmd_line);
    		return rc;
    	}
    	
    	while(1){
       		printf("%s", SH_PROMPT);
            if (fgets(cmd_line, ARG_MAX, stdin) == NULL){
            	printf("\n");
                break;
            }
            //remove the trailing \n from cmd_buff
            cmd_line[strcspn(cmd_line,"\n")] = '\0';
    
        	//IMPLEMENT THE REST OF THE REQUIREMENTS
    		if (strlen(cmd_line) == 0) {
    			printf(CMD_WARN_NO_CMD);
    			continue;
    		}
    
    		if ((rc = build_cmd_list(cmd_line, clist, rc)) != OK) break;
    
    		if ((rc = exec_cmd(clist, rc)) != OK) {
    			if (rc == OK_EXIT) rc = OK;
    			break;
    		}
        	
        	clear_cmd_list(clist);
        }
        
        free(cmd_line);
        free_cmd_list(clist);
        free(clist);
        return rc;
    }