Skip to content
Snippets Groups Projects
Commit 73bf2e50 authored by Wendy Nguyen's avatar Wendy Nguyen
Browse files

Upload A3

parent 176e5cb0
No related branches found
No related tags found
No related merge requests found
{
"files.associations": {
"ctype.h": "c"
}
}
\ No newline at end of file
File added
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dshlib.h"
/*
* Implement your main 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. Since we want fgets to also handle
* end of file so we can run this headless for testing we need to check
* the return code of fgets. I have provided an example below of how
* to do this assuming you are storing user input inside of the cmd_buff
* variable.
*
* 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
*
* Expected output:
*
* CMD_OK_HEADER if the command parses properly. You will
* follow this by the command details
*
* CMD_WARN_NO_CMD if the user entered a blank command
* CMD_ERR_PIPE_LIMIT if the user entered too many commands using
* the pipe feature, e.g., cmd1 | cmd2 | ... |
*
* See the provided test cases for output expectations.
*/
void _exit(char *buff) {
free(buff);
exit(OK);
}
int main()
{
char *cmd_buff = malloc(ARG_MAX);
command_list_t *clist = malloc(sizeof(command_list_t));
int commandCount = 0;
int code = 0;
while(1){
clist->num = 0;
printf("%s", SH_PROMPT);
if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){
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(cmd_buff);
}
else {
// printf("Command line: %s\n", cmd_buff);
code = build_cmd_list(cmd_buff, clist);
}
}
free(clist);
}
\ No newline at end of file
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "dshlib.h"
/*
* build_cmd_list
* cmd_line: the command line from the user
* clist *: pointer to clist structure to be populated
*
* This function builds the command_list_t structure passed by the caller
* It does this by first splitting the cmd_line into commands by spltting
* the string based on any pipe characters '|'. It then traverses each
* command. For each command (a substring of cmd_line), it then parses
* that command by taking the first token as the executable name, and
* then the remaining tokens as the arguments.
*
* NOTE your implementation should be able to handle properly removing
* leading and trailing spaces!
*
* errors returned:
*
* OK: No Error
* ERR_TOO_MANY_COMMANDS: There is a limit of CMD_MAX (see dshlib.h)
* commands.
* ERR_CMD_OR_ARGS_TOO_BIG: One of the commands provided by the user
* was larger than allowed, either the
* executable name, or the arg string.
*
* Standard Library Functions You Might Want To Consider Using
* memset(), strcmp(), strcpy(), strtok(), strlen(), strchr()
*/
void printDragon(){
const char dragon[39][103] = {
" @%%%% \n",
" %%%%%% \n",
" %%%%%% \n",
" % %%%%%%% @ \n",
" %%%%%%%%%% %%%%%%% \n",
" %%%%%%% %%%%@ %%%%%%%%%%%%@ %%%%%% @%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%% %%%%%%%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%% %%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%% %% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@ \n",
" %%%%%%%%@ %%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% %% \n",
" %%%%%%%%%%%%% %%@%%%%%%%%%%%% %%%%%%%%%%% %%%%%%%%%%%% @% \n",
" %%%%%%%%%% %%% %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% \n",
" %%%%%%%%% % %%%%%%%%%%%%% %%%%%%%%%%%%@%%%%%%%%%%% \n",
"%%%%%%%%%@ % %%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%% \n",
"%%%%%%%%@ %%@%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%%%% \n",
"%%%%%%%@ %%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \n",
"%%%%%%%%%% %%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% \n",
"%%%%%%%%%@ @%%%%%%%%%%%%%% %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%% %%%%%%%%\n",
"%%%%%%%%%% %%%%%%%%%%%%%%%%% %%%%%%%%%%%%% %%%%%%%%%%%%%%%%%% %%%%%%%%%\n",
"%%%%%%%%%@%%@ %%%%%%%%%%%%%%%%@ %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %%\n",
" %%%%%%%%%% % %%%%%%%%%%%%%%@ %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% %%\n",
" %%%%%%%%%%%% @ %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% \n",
" %%%%%%%%%%%%% %% % %@ %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% \n",
" %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% \n",
" @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%% %%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%% @%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%% @%@% @%%%%%%%%%%%%%%%%%% %%% \n",
" %%%%%%%%%%%%%%% %%%%%%%%%% %%%%%%%%%%%%%%% % \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%% \n",
" %%%%%%%%%%%%%%%%%%%%%%%%%% %%%% %%% %%%%%%%%%% %%%@ \n",
" %%%%%%%%%%%%%%%%%%% %%%%%% %% %%%%%%%%%%%%%@ \n",
" %%%%%%%@ \n"
};
for (int i = 0; i < sizeof(dragon) / sizeof(dragon[0]); i++) {
for (int j = 0; j < sizeof(dragon[0]) / sizeof(dragon[0][0]); j++) {
if (dragon[i][j] == 0) break;
putchar((char)dragon[i][j]);
}
}
}
int build_cmd_list(char *cmd_line, command_list_t *clist)
{
if (strlen(cmd_line) == 0) {
printf(CMD_WARN_NO_CMD);
return OK;
}
// get the first command
char* token;
token = strtok(cmd_line, PIPE_STRING);
// keep getting commands until token is NULL
while (token != 0) {
// trim empty space at the start and end of command
while (*token == SPACE_CHAR) token++;
if (token[strlen(token) - 1] == SPACE_CHAR) token[strlen(token) - 1] = '\0';
command_t* command = malloc(sizeof(command_t));
if (strlen(token) > SH_CMD_MAX) {
return ERR_CMD_OR_ARGS_TOO_BIG;
}
strcpy(command->exe,token);
clist->commands[clist->num] = *command;
clist->num += 1;
// printf("Current: %s\n", token);
token = strtok(0, PIPE_STRING);
}
if (clist->num > CMD_MAX) {
printf("dsh> ");
printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
return ERR_TOO_MANY_COMMANDS;
}
printf(CMD_OK_HEADER, clist->num);
// printf("Number of commands read in: %d\n", clist->num);
for (int i = 0; i < clist->num; i++){
// printf("Command #%d: %s\n", i+1, clist->commands[i].exe);
// char *exe = malloc(EXE_MAX * sizeof(char));
// char *args = malloc(ARG_MAX * sizeof(char));
// exe = strtok(clist->commands[i].exe, " ");
// args = strtok(0, "");
char *exe = strtok(clist->commands[i].exe, " ");
char *args = strtok(NULL, "");
if (exe) {
if (strcmp(exe, "dragon") == 0) {
printDragon();
}
strcpy(clist->commands[i].exe, exe);
}
if (args) {
strcpy(clist->commands[i].args, args);
printf("<%d> %s [%s]\n", i+1, clist->commands[i].exe, clist->commands[i].args);
} else {
printf("<%d> %s\n", i+1, clist->commands[i].exe);
}
// printf("Exe: %s\t Arguments: %s\n", clist->commands[i].exe, clist->commands[i].args);
}
return OK;
}
\ No newline at end of file
#ifndef __DSHLIB_H__
#define __DSHLIB_H__
// Constants for command structure sizes
#define EXE_MAX 64
#define ARG_MAX 256
#define CMD_MAX 8
// Longest command that can be read from the shell
#define SH_CMD_MAX EXE_MAX + ARG_MAX
typedef struct command
{
char exe[EXE_MAX];
char args[ARG_MAX];
} command_t;
typedef struct command_list
{
int num;
command_t commands[CMD_MAX];
} command_list_t;
// Special character #defines
#define SPACE_CHAR ' '
#define PIPE_CHAR '|'
#define PIPE_STRING "|"
#define SH_PROMPT "dsh> "
#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
// starter code
#define M_NOT_IMPL "The requested operation is not implemented yet!\n"
#define EXIT_NOT_IMPL 3
#define NOT_IMPLEMENTED_YET 0
// prototypes
int build_cmd_list(char *cmd_line, command_list_t *clist);
// 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"
#endif
\ No newline at end of file
# 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:
./test.sh
# Phony targets
.PHONY: all clean
\ No newline at end of file
#!/usr/bin/env bats
@test "Simple Command" {
run ./dsh <<EOF
test_command
exit
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="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>test_commanddsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "Simple Command with Args" {
run ./dsh <<EOF
cmd -a1 -a2
exit
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '[:space:]')
# Expected output
expected_output="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>cmd[-a1-a2]dsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "No command provided" {
run ./dsh <<EOF
exit
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="dsh>warning:nocommandsprovideddsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "Two commands" {
run ./dsh <<EOF
command_one | command_two
exit
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="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS2<1>command_one<2>command_twodsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "three commands with args" {
run ./dsh <<EOF
cmd1 a1 a2 a3 | cmd2 a4 a5 a6 | cmd3 a7 a8 a9
exit
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="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS3<1>cmd1[a1a2a3]<2>cmd2[a4a5a6]<3>cmd3[a7a8a9]dsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "try max (8) commands" {
run ./dsh <<EOF
cmd1 | cmd2 | cmd3 | cmd4 | cmd5 | cmd6 | cmd7 | cmd8
exit
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="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS8<1>cmd1<2>cmd2<3>cmd3<4>cmd4<5>cmd5<6>cmd6<7>cmd7<8>cmd8dsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "try too many commands" {
run ./dsh <<EOF
cmd1 | cmd2 | cmd3 | cmd4 | cmd5 | cmd6 | cmd7 | cmd8 | cmd9
exit
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="dsh>error:pipinglimitedto8commandsdsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "kitchen sink - multiple commands" {
run ./dsh <<EOF
cmd1
cmd2 arg arg2
p1 | p2
p3 p3a1 p3a2 | p4 p4a1 p4a2
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="dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>cmd1dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS1<1>cmd2[argarg2]dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS2<1>p1<2>p2dsh>PARSEDCOMMANDLINE-TOTALCOMMANDS2<1>p3[p3a1p3a2]<2>p4[p4a1p4a2]dsh>"
# 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"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment