Select Git revision
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 <errno.h>
#include "dshlib.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 last_rc = 0;
int execute_pipeline(command_list_t *clist) {
if (!clist->num) return OK;
if (clist->num == 1) {
char *cmd = clist->commands[0].argv[0];
if (clist->commands[0].argc > 0) {
if (!strcmp(cmd, EXIT_CMD)) return OK_EXIT;
if (!strcmp(cmd, "cd")) {
handle_cd(clist->commands[0].argc > 1 ? clist->commands[0].argv[1] : NULL);
return OK;
}
if (!strcmp(cmd, "rc")) {
printf("%d\n", last_rc);
return OK;
}
}
}
pid_t *child_pids = malloc(clist->num * sizeof(pid_t));
if (!child_pids) {
fprintf(stderr, "Memory allocation failed\n");
return ERR_MEMORY;
}
int pipes[CMD_MAX - 1][2];
for (int i = 0; i < clist->num - 1; i++) {
if (pipe(pipes[i]) == -1) {
perror("pipe");
while (i--) close(pipes[i][0]), close(pipes[i][1]);
free(child_pids);
return ERR_EXEC_CMD;
}
}
for (int i = 0; i < clist->num; i++) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
for (int j = 0; j < clist->num - 1; j++) close(pipes[j][0]), close(pipes[j][1]);
free(child_pids);
return ERR_EXEC_CMD;
}
if (!pid) {
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(errno);
}
child_pids[i] = pid;
}
for (int i = 0; i < clist->num - 1; i++) close(pipes[i][0]), close(pipes[i][1]);
for (int i = 0, status; i < clist->num; i++) {
waitpid(child_pids[i], &status, 0);
if (i == clist->num - 1) last_rc = WEXITSTATUS(status);
}
free(child_pids);
return OK;
}
int build_cmd_list(char *cmd_line, command_list_t *clist) {
int ret = OK;
char *cmd_copy = strdup(cmd_line);
if (!cmd_copy) {
fprintf(stderr, "Memory allocation failed\n");
return ERR_MEMORY;
}
clist->num = 0;
char *token = NULL, *context = NULL;
token = strtok_r(cmd_copy, PIPE_STRING, &context);
while (token != NULL) {
if (clist->num >= CMD_MAX) {
ret = ERR_TOO_MANY_COMMANDS;
fprintf(stderr, CMD_ERR_PIPE_LIMIT, CMD_MAX);
break;
}
if (alloc_cmd_buff(&clist->commands[clist->num]) != OK) {
ret = ERR_MEMORY;
break;
}
/* Copy token into the command buffer */
strncpy(clist->commands[clist->num]._cmd_buffer, token, SH_CMD_MAX);
clist->commands[clist->num]._cmd_buffer[SH_CMD_MAX] = '\0';
if (build_cmd_buff(clist->commands[clist->num]._cmd_buffer,
&clist->commands[clist->num]) != OK) {
free_cmd_buff(&clist->commands[clist->num]);
ret = ERR_CMD_ARGS_BAD;
break;
}
clist->num++;
token = strtok_r(NULL, PIPE_STRING, &context);
}
free(cmd_copy);
if (ret != OK) {
for (int i = 0; i < clist->num; i++) {
free_cmd_buff(&clist->commands[i]);
}
return ret;
}
if (clist->num == 0) {
fprintf(stderr, CMD_WARN_NO_CMD);
return WARN_NO_CMDS;
}
return OK;
}
int free_cmd_list(command_list_t *clist) {
if (clist == NULL) {
return ERR_MEMORY;
}
for (int i = 0; i < clist->num; i++) {
free_cmd_buff(&clist->commands[i]);
}
clist->num = 0;
return OK;
}
int exec_local_cmd_loop() {
cmd_buff_t cmd;
command_list_t cmd_list;
int rc;
if ((rc = alloc_cmd_buff(&cmd)) != OK) {
fprintf(stderr, "Memory allocation failed\n");
return ERR_MEMORY;
}
for (;;) {
printf("%s", SH_PROMPT);
if (!fgets(cmd._cmd_buffer, SH_CMD_MAX, stdin)) {
putchar('\n');
break;
}
cmd._cmd_buffer[strcspn(cmd._cmd_buffer, "\n")] = '\0';
char *trimmed = cmd._cmd_buffer;
while (*trimmed && isspace((unsigned char)*trimmed)) trimmed++;
if (!*trimmed) {
puts(CMD_WARN_NO_CMD);
continue;
}
if (!strcmp(trimmed, EXIT_CMD)) break;
if (!strcmp(trimmed, "dragon")) {
print_dragon();
continue;
}
if ((rc = build_cmd_list(trimmed, &cmd_list)) != OK) {
if (rc != WARN_NO_CMDS) fprintf(stderr, "Error parsing command: %d\n", rc);
continue;
}
rc = execute_pipeline(&cmd_list);
free_cmd_list(&cmd_list);
if (rc == OK_EXIT) break;
}
free_cmd_buff(&cmd);
return OK;
}
int handle_cd(char *path) {
if (path == NULL)
return 0;
if (chdir(path) != 0) {
perror("cd");
return -1;
}
return 0;
}
int alloc_cmd_buff(cmd_buff_t *cmd) {
if (cmd == NULL) return ERR_MEMORY;
cmd->_cmd_buffer = malloc(SH_CMD_MAX + 1); // +1 for the null terminator
if (cmd->_cmd_buffer == NULL) return ERR_MEMORY;
return OK;
}
int free_cmd_buff(cmd_buff_t *cmd) {
if (cmd == NULL) return ERR_MEMORY;
free(cmd->_cmd_buffer);
cmd->_cmd_buffer = NULL;
return OK;
}
int clear_cmd_buff(cmd_buff_t *cmd) {
if (cmd == NULL) return ERR_MEMORY;
cmd->argc = 0;
for (int i = 0; i < CMD_ARGV_MAX; i++) {
cmd->argv[i] = NULL;
}
return OK;
}
int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd) {
if (cmd_line == NULL || cmd == NULL)
return ERR_MEMORY;
clear_cmd_buff(cmd);
int tokenCount = 0;
char *ptr = cmd_line;
while (*ptr) {
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
if (!*ptr)
break;
char *token = ptr;
if (*ptr == '"' || *ptr == '\'') {
char quote = *ptr;
ptr++;
token = ptr;
while (*ptr && *ptr != quote)
ptr++;
if (*ptr == quote) {
*ptr = '\0';
ptr++;
}
} else {
while (*ptr && !isspace((unsigned char)*ptr))
ptr++;
if (*ptr) {
*ptr = '\0';
ptr++;
}
}
cmd->argv[tokenCount++] = token;
if (tokenCount >= CMD_ARGV_MAX - 1)
break;
}
cmd->argv[tokenCount] = NULL;
cmd->argc = tokenCount;
return tokenCount == 0 ? WARN_NO_CMDS : OK;
}