Skip to content
Snippets Groups Projects
Commit ea9272dc authored by cxb23's avatar cxb23
Browse files

finished assignment 4

parent a301a5ae
Branches
No related tags found
No related merge requests found
File added
#!/usr/bin/env bats
############################ DO NOT EDIT THIS FILE #####################################
# File: assignement_tests.sh
#
# DO NOT EDIT THIS FILE
#
# Add/Edit Student tests in student_tests.sh
#
# All tests in this file must pass - it is used as part of grading!
########################################################################################
@test "Change directory" {
current=$(pwd)
cd /tmp
mkdir -p dsh-test
run "${current}/dsh" <<EOF
cd dsh-test
pwd
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '[:space:]')
# Expected output with all whitespace removed for easier matching
expected_output="/tmp/dsh-testdsh2>dsh2>dsh2>cmdloopreturned0"
# These echo commands will help with debugging and will only print
#if the test fails
echo "Captured stdout:"
echo "Output: $output"
echo "Exit Status: $status"
echo "${stripped_output} -> ${expected_output}"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "Change directory - no args" {
current=$(pwd)
cd /tmp
mkdir -p dsh-test
run "${current}/dsh" <<EOF
cd
pwd
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '[:space:]')
# Expected output with all whitespace removed for easier matching
expected_output="/tmpdsh2>dsh2>dsh2>cmdloopreturned0"
# These echo commands will help with debugging and will only print
#if the test fails
echo "Captured stdout:"
echo "Output: $output"
echo "Exit Status: $status"
echo "${stripped_output} -> ${expected_output}"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "Which which ... which?" {
run "./dsh" <<EOF
which which
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '[:space:]')
# Expected output with all whitespace removed for easier matching
expected_output="/usr/bin/whichdsh2>dsh2>cmdloopreturned0"
# These echo commands will help with debugging and will only print
#if the test fails
echo "Captured stdout:"
echo "Output: $output"
echo "Exit Status: $status"
echo "${stripped_output} -> ${expected_output}"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
}
@test "It handles quoted spaces" {
run "./dsh" <<EOF
echo " hello world "
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '\t\n\r\f\v')
# Expected output with all whitespace removed for easier matching
expected_output=" hello world dsh2> dsh2> cmd loop returned 0"
# These echo commands will help with debugging and will only print
#if the test fails
echo "Captured stdout:"
echo "Output: $output"
echo "Exit Status: $status"
echo "${stripped_output} -> ${expected_output}"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
}
#!/usr/bin/env bats
# File: student_tests.sh
#
# Create your unit tests suit in this file
@test "Example: check ls runs without errors" {
run ./dsh <<EOF
ls
EOF
# Assertions
[ "$status" -eq 0 ]
}
#include <stdio.h>
#include <stdlib.h>
#include "dshlib.h"
// EXTRA CREDIT - print the drexel dragon from the readme.md
extern 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);
}
@%%%%
%%%%%%
%%%%%%
% %%%%%%% @
%%%%%%%%%% %%%%%%%
%%%%%%% %%%%@ %%%%%%%%%%%%@ %%%%%% @%%%%
%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%% %%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%% %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%% %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@
%%%%%%%%@ %%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% %%
%%%%%%%%%%%%% %%@%%%%%%%%%%%% %%%%%%%%%%% %%%%%%%%%%%% @%
%%%%%%%%%% %%% %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%% % %%%%%%%%%%%%% %%%%%%%%%%%%@%%%%%%%%%%%
%%%%%%%%%@ % %%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%@ %%@%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%@ %%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%% %%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%
%%%%%%%%%@ @%%%%%%%%%%%%%% %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%% %%%%%%%%
%%%%%%%%%% %%%%%%%%%%%%%%%%% %%%%%%%%%%%%% %%%%%%%%%%%%%%%%%% %%%%%%%%%
%%%%%%%%%@%%@ %%%%%%%%%%%%%%%%@ %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %%
%%%%%%%%%% % %%%%%%%%%%%%%%@ %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% %%
%%%%%%%%%%%% @ %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%
%%%%%%%%%%%%% %% % %@ %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%
%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%
@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%% %%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%% @%%%%%%%%%
%%%%%%%%%%%%%%%%%%%% @%@% @%%%%%%%%%%%%%%%%%% %%%
%%%%%%%%%%%%%%% %%%%%%%%%% %%%%%%%%%%%%%%% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% %%% %%%%%%%%%% %%%@
%%%%%%%%%%%%%%%%%%% %%%%%% %% %%%%%%%%%%%%%@
%%%%%%%@
File added
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dshlib.h"
/* DO NOT EDIT
* main() logic moved to exec_local_cmd_loop() in dshlib.c
*/
int main(){
int rc = exec_local_cmd_loop();
printf("cmd loop returned %d\n", rc);
}
#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;
}
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) {
free(cmd_buff);
return ERR_MEMORY;
}
for (int i = 0; i < CMD_ARGV_MAX; i++) {
cmd_buff->argv[i] = (char *)malloc(ARG_MAX);
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);
if (cmd_buff->_cmd_buffer == NULL) {
free(cmd_buff->_cmd_buffer);
for (int i = 0; i < CMD_ARGV_MAX; i++) {
free(cmd_buff->argv[i]);
}
free(cmd_buff->argv);
return ERR_MEMORY;
}
return OK_EXIT;
}
int free_cmd_buff(cmd_buff_t *cmd_buff) {
free(cmd_buff->_cmd_buffer);
for (int i = 0; i < CMD_ARGV_MAX - 1; i++) free(cmd_buff->argv[i]);
free(cmd_buff);
return OK_EXIT;
}
int clear_cmd_buff(cmd_buff_t *cmd_buff) {
cmd_buff->argc = 0;
free(cmd_buff->_cmd_buffer);
for (int i = 0; i < CMD_ARGV_MAX; i++) cmd_buff->argv[i] = NULL;
return OK_EXIT;
}
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 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;
cmd_buff->_cmd_buffer = strdup(trim_whitespace(cmd_line));
if (cmd_buff->_cmd_buffer == NULL) {
free(cmd_buff);
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) cmd_buff->argv[cmd_buff->argc++] = p;
cmd_buff->argv[cmd_buff->argc] = NULL;
return OK_EXIT;
}
/*
* 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_buff = malloc(ARG_MAX * sizeof(char));
int rc = 0;
cmd_buff_t *cmd = malloc(CMD_ARGV_MAX * sizeof(char *));
if ((rc = alloc_cmd_buff(cmd)) != OK_EXIT) exit(rc);
// TODO IMPLEMENT MAIN LOOP
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
if (strcmp(cmd_buff, EXIT_CMD) == 0) {
exit(rc);
}
if (strcmp(cmd_buff, "rc") == 0) {
printf("%d\n", rc);
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
continue;
}
if (strcmp(cmd_buff, "\0") == 0) {
rc = WARN_NO_CMDS;
printf(CMD_WARN_NO_CMD);
//if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
continue;
}
// TODO IMPLEMENT parsing input to cmd_buff_t *cmd_buff
if ((rc = build_cmd_buff(cmd_buff, cmd)) != OK_EXIT) {
exit(rc);
rc = 0;
}
/*
if (strcmp(cmd->argv[0], "echo") == 0) {
for (int i = 1; i < CMD_ARGV_MAX; i++) {
printf("%s ", cmd->argv[i]);
}
printf("\n");
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
continue;
}*/
// TODO IMPLEMENT if built-in command, execute builtin logic for exit, cd (extra credit: dragon)
if (strcmp(cmd_buff, "dragon") == 0) {
print_dragon();
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
rc = 0;
continue;
}
// the cd command should chdir to the provided directory; if no directory is provided, do nothing
if (strcmp(cmd->argv[0], "cd") == 0) {
if (cmd->argc < 2) {
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
rc = 0;
continue;
}
if (chdir(cmd->argv[1]) != 0) {
perror("chdir failed");
rc = 1;
}
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
rc = 0;
continue;
}
// TODO IMPLEMENT if not built-in command, fork/exec as an external command
// for example, if the user input is "ls -l", you would fork/exec the command "ls" with the arg "-l"
pid_t pid = fork();
if (pid < 0) {
printf("fork failed");
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
continue;
} else if (pid == 0) {
execvp(cmd->argv[0], cmd->argv);
perror(CMD_ERR_EXECUTE);
exit(1);
} else {
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
} else {
rc = 1;
}
}
if ((rc = clear_cmd_buff(cmd)) != OK_EXIT) exit(rc);
}
free(cmd_buff);
free_cmd_buff(cmd);
return OK;
}
#ifndef __DSHLIB_H__
#define __DSHLIB_H__
//Constants for command structure sizes
#define EXE_MAX 64
#define ARG_MAX 256
#define CMD_MAX 8
#define CMD_ARGV_MAX (CMD_MAX + 1)
// Longest command that can be read from the shell
#define SH_CMD_MAX EXE_MAX + ARG_MAX
typedef struct cmd_buff
{
int argc;
char **argv;
char *_cmd_buffer;
} cmd_buff_t;
/* WIP - Move to next assignment
#define N_ARG_MAX 15 //MAX number of args for a command
typedef struct command{
char exe [EXE_MAX];
char args[ARG_MAX];
int argc;
char *argv[N_ARG_MAX + 1]; //last argv[LAST] must be \0
}command_t;
*/
//Special character #defines
#define SPACE_CHAR ' '
#define SPACE_STRING " "
#define PIPE_CHAR '|'
#define PIPE_STRING "|"
#define DOUBLE_QUOTE_CHAR '"'
#define SH_PROMPT "dsh2> "
#define EXIT_CMD "exit"
//Standard Return Codes
#define OK 0
#define WARN_NO_CMDS -1
#define ERR_TOO_MANY_COMMANDS -2
#define ERR_CMD_OR_ARGS_TOO_BIG -3
#define ERR_CMD_ARGS_BAD -4 //for extra credit
#define ERR_MEMORY -5
#define ERR_EXEC_CMD -6
#define OK_EXIT -7
//prototypes
int alloc_cmd_buff();
int free_cmd_buff(cmd_buff_t *);
int clear_cmd_buff(cmd_buff_t *);
int build_cmd_buff(char *, cmd_buff_t *);
void exeCD(char *path);
void execFork(char *);
extern void print_dragon();
//built in command stuff
typedef enum {
BI_CMD_EXIT,
BI_CMD_DRAGON,
BI_CMD_CD,
BI_NOT_BI,
BI_EXECUTED,
BI_RC,
} Built_In_Cmds;
Built_In_Cmds match_command(const char *input);
Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd);
//main execution context
int exec_local_cmd_loop();
int exec_cmd(cmd_buff_t *cmd);
//output constants
#define CMD_OK_HEADER "PARSED COMMAND LINE - TOTAL COMMANDS %d\n"
#define CMD_WARN_NO_CMD "warning: no commands provided\n"
#define CMD_ERR_PIPE_LIMIT "error: piping limited to %d commands\n"
#define CMD_ERR_EXECUTE "Command not found in PATH\n"
#endif
# Compiler settings
CC = gcc
CFLAGS = -Wall -Wextra -g
# Target executable name
TARGET = dsh
# Find all source and header files
SRCS = $(wildcard *.c)
HDRS = $(wildcard *.h)
# Default target
all: $(TARGET)
# Compile source to executable
$(TARGET): $(SRCS) $(HDRS)
$(CC) $(CFLAGS) -o $(TARGET) $(SRCS)
# Clean up build files
clean:
rm -f $(TARGET)
test:
bats $(wildcard ./bats/*.sh)
valgrind:
echo "pwd\nexit" | valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./$(TARGET)
echo "pwd\nexit" | valgrind --tool=helgrind --error-exitcode=1 ./$(TARGET)
# Phony targets
.PHONY: all clean test
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment