Select Git revision
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;
}