Removed submodules

This commit is contained in:
2026-05-31 14:34:00 -04:00
commit 46c36b11da
352 changed files with 14792 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# Ignore all compiled files regardless of location
**/*.o
# But not the private.o
!tests/private.o
# Ignore executables for this project
shell
testsuite
# Ignore test outputs
tests/ckstyle
tests/itests.txt
tests/outputs
tests/style.txt
tests/valgrind
tests/utests.txt
**/.nfs*
**/.vscode
+51
View File
@@ -0,0 +1,51 @@
#
# Simple Makefile
# Mike Lam, James Madison University, August 2016
#
# This makefile builds a simple application that contains a main module
# (specified by the EXE variable) and a predefined list of additional modules
# (specified by the MODS variable). If there are any external library
# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable.
# If there are any precompiled object files, list them in the OBJS variable.
#
# By default, this makefile will build the project with debugging symbols and
# without optimization. To change this, edit or remove the "-g" and "-O0"
# options in CFLAGS and LDFLAGS accordingly.
#
# By default, this makefile build the application using the GNU C compiler,
# adhering to the C99 standard with all warnings enabled.
# application-specific settings and run target
EXE=shell
MODS=model.o effects.o
OBJS=
LIBS=
default: $(EXE)
test: $(EXE)
make -C tests test
# compiler/linker settings
CC=gcc
CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic
LDFLAGS=-g -O0
# build targets
$(EXE): main.o $(MODS) $(OBJS)
$(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS)
%.o: %.c
$(CC) -c $(CFLAGS) $<
clean:
rm -f $(EXE) main.o $(MODS)
make -C tests clean
.PHONY: default clean
+88
View File
@@ -0,0 +1,88 @@
#define _POSIX_C_SOURCE 200809L // needed for strdup extension
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "effects.h"
#include "model.h"
/* Executed when starting to process a new command line. The fsm_t
should have been updated to include a pointer to the current token.
For instance, if the command line was "ls -l data NL", the fsm_t
has a field that points to "ls". */
void
start_command (fsm_t *cmdmodel)
{
printf ("Starting new command: %s\n", cmdmodel->current_token);
// TODO: Copy the current token to store it in the FSM's command
// field. Next, create the FSM's args array (length MAX_ARGUMENTS)
// set the current token as args[0], and initialize nargs to be
// the number of arguments (1 at this point).
// Allocate args array (NULL-initialized) if not already
cmdmodel->args = calloc (MAX_ARGUMENTS, sizeof (char *));
cmdmodel->command = cmdmodel->current_token;
cmdmodel->args[0] = cmdmodel->current_token;
cmdmodel->nargs = 1;
}
/* Executed when processing a token after the command name. For instance,
if the command line was "ls -l data NL", this function will be called
when the current token is "-l" and again when it is "data". */
void
append (fsm_t *cmdmodel)
{
if (cmdmodel->nargs >= MAX_ARGUMENTS)
return;
printf ("Appending %s to the argument list\n", cmdmodel->current_token);
assert (cmdmodel->args != NULL);
// TODO: Store the current token into the args array and increment nargs
cmdmodel->args[cmdmodel->nargs++] = cmdmodel->current_token;
}
/* Executed when either a NL or | (pipe) is encountered. For instance, if
the command line is "ls -l data NL", the current token will be "NL"; also,
the FSM's args array should be complete, containing "ls", "-l", and "data",
followed by several NULL pointers. */
void
execute (fsm_t *cmdmodel)
{
assert (cmdmodel->args != NULL);
// TODO: Print out the argument list similar to the format shown and free
// the args array.
printf ("Execute %s with arguments { ", cmdmodel->args[0]);
for (uint8_t i = 0; i < cmdmodel->nargs; i++)
{
printf ("%s, ", cmdmodel->args[i]);
}
printf ("(null) }\n");
}
// No changes are needed to the effects below
void
link_commands (fsm_t *cmdmodel)
{
printf ("Set up pipe\n");
execute (cmdmodel);
}
void
error_pipe (fsm_t *cmdmodel)
{
printf ("ERROR: Received token %s while in state %s\n",
cmdmodel->current_token, state_name (cmdmodel->state));
}
void
error_newline (fsm_t *cmdmodel)
{
printf ("ERROR: Received token %s while in state %s\n",
cmdmodel->current_token, state_name (cmdmodel->state));
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef __effects_h__
#define __effects_h__
#include "model.h"
void start_command (fsm_t *);
void append (fsm_t *);
void execute (fsm_t *);
void link_commands (fsm_t *);
void error_pipe (fsm_t *);
void error_newline (fsm_t *);
#endif
+61
View File
@@ -0,0 +1,61 @@
/*
* CS 361: Template lab driver
*
* Name:
*/
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "model.h"
void
usage (void)
{
printf ("shell, a command line state model simulator\n\n");
printf ("usage: shell cmd [arg ...] [ | cmd [arg ...] ... ]\n");
printf ("Each cmd can contain an argument list of variable length\n");
printf ("Each cmd after the first must be preceded by a pipe | character\n");
}
int
main (int argc, char **argv)
{
// With no arguments, show usage and exit
if (argc != 2)
{
usage ();
return EXIT_FAILURE;
}
// Declare and initialize a FSM
fsm_t *cmdmodel = cmdline_init ();
if (cmdmodel == NULL)
return EXIT_FAILURE;
// TODO: Change this to split the string into tokens, where
// each token is an event that needs to be handled. After
// looking up the event number, store the token in the FSM
// and call handle_event().
char *token = strtok (argv[1], " ");
while (token != NULL)
{
cmdmodel->current_token = token;
event_t event = lookup (token);
if (!handle_event (cmdmodel, event))
break;
token = strtok (NULL, " ");
}
// Free remaining allocated data
if (cmdmodel->args != NULL)
free (cmdmodel->args);
free (cmdmodel);
return EXIT_SUCCESS;
}
+130
View File
@@ -0,0 +1,130 @@
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "effects.h"
#include "model.h"
// TODO: Complete this table mapping state/events to the target state. Each
// row corresponds to the current state. Each column within that row indicates
// the next state for a given event. If there is no transition defined, that
// entry should be NST ("no state").
static state_t const _transitions[NUM_STATES][NUM_EVENTS]
= { { Command, Term, NST },
{ Arguments, Make_Pipe, Term },
{ Arguments, Make_Pipe, Term },
{ Command, Term, Term },
{ NST, NST, NST } };
// TODO: Create a table mapping states/events to the effect functions. If
// there is no valid transition, the entry here would be NULL because actions
// are function pointers.
static action_t const _effects[NUM_STATES][NUM_EVENTS]
= { { start_command, error_pipe, NULL },
{ append, link_commands, execute },
{ append, link_commands, execute },
{ start_command, error_pipe, error_newline },
{ NULL, NULL, NULL } };
/* Create an instance of an FSM and initialize its fields as appropriate.
Some fields are common to most FSMs (such as an initial state or a
pointer to a transition function). Other fields will be specific to
this fsm_t declaration. Return NULL if any part of the initialization
fails. */
fsm_t *
cmdline_init (void)
{
fsm_t *fsm = calloc (1, sizeof (fsm_t));
fsm->state = Init;
fsm->transition = transition;
return fsm;
}
// TODO: Create a transition function that is specific to this type
// of FSM. This function needs to take an fsm_t* and an event, returning
// both the new state and the effect to perform (the latter is returned
// using a call-by-reference parameter. This function should NOT contain
// any "if" types of statements based on the state or event; it should
// simply lookup these values in the tables defined above.
state_t
transition (struct fsm *fsm, event_t event, action_t *effect)
{
assert (fsm->state < NST);
assert (event < NIL);
*effect = _effects[fsm->state][event];
return _transitions[fsm->state][event];
}
/* Helper function for providing a printable string name for an event */
const char *
event_name (event_t evt)
{
assert (evt <= NIL);
// Event names for printing out
const char *names[] = { "TOKEN", "PIPE", "NEWLINE", "NIL" };
return names[evt];
}
/* Helper function for providing a printable string name for an state */
const char *
state_name (state_t st)
{
assert (st <= NST);
// State names for printing out
const char *names[]
= { "Init", "Command", "Arguments", "Make_Pipe", "Term", "NST" };
return names[st];
}
/* Generic front-end for handling events. Should do nothing more
than calling the FSM's transition function, performing an effect
(if appropriate) and updating the state. Return false if the new
state is the terminal state. */
bool
handle_event (fsm_t *fsm, event_t event)
{
assert (fsm != NULL);
// TODO: Look up the current state/event combination in the
// transition table. Print the following line for debugging
// purposes just for this lab. This should be printed even
// if there is no transition.
action_t effect;
state_t new_state = fsm->transition (fsm, event, &effect);
printf ("[%s.%s -> %s]\n", state_name (fsm->state), event_name (event),
state_name (new_state));
// TODO: If the state/event combination is valid, execute
// the transition and effect function (if there is one).
// If the next state is Term (terminated), return false.
// Otherwise return true.
if (new_state != NST)
{
if (effect != NULL)
{
effect (fsm);
}
fsm->state = new_state;
}
return fsm->state != Term;
}
/* Given a string, return the event type. Do not modify this function. */
event_t
lookup (char *token)
{
if (!strcmp (token, "|"))
return PIPE;
if (!strcmp (token, "NL"))
return NEWLINE;
return TOKEN;
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef __model_h__
#define __model_h__
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
// Generic definitions for any type of statemodel
// States and events should just be integers
typedef int state_t;
typedef int event_t;
// Needed for circular typedef. This lets action_t use fsm_t in its parameter
// list, while the struct fsm can use action_t as a field.
typedef struct fsm fsm_t;
// All entry, exit, and effect instances use the action type
typedef void (*action_t) (fsm_t *);
// Each FSM instance contains a current state
struct fsm
{
state_t state; // current state
// pointer to the FSM's transition function
state_t (*transition) (struct fsm *, event_t, action_t *);
// Additional data fields specific to this FSM
char *command; // the name of the command to run
size_t nargs; // the number of command-line arguments
char **args; // the command-line arguments
char *current_token; // current token being processed
};
// Generic entry point for handling events
bool handle_event (fsm_t *, event_t);
// Additional definitions specific to an FSM for command line processing
#define MAX_ARGUMENTS 10
// Events
typedef enum
{
TOKEN, // normal command-line token
PIPE, // vertical bar character
NEWLINE, // newline at the end of the command
NIL // invalid non-event
} cmdevt_t;
#define NUM_EVENTS NIL
// States
typedef enum
{
Init, // initial state
Command, // establishing the command name
Arguments, // building the argument list
Make_Pipe, // linking the commands together for a pipe
Term, // terminal state (execute program or error)
NST // invalid non-state
} cmdst_t;
#define NUM_STATES NST
// Helper functions
fsm_t *cmdline_init (void); // initialize the FSM
state_t transition (struct fsm *fsm, event_t event, action_t *effect);
event_t lookup (char *); // convert an event string to its numeric value
// Translate event/state numbers to their string equivalent
const char *event_name (event_t);
const char *state_name (state_t);
#endif
+88
View File
@@ -0,0 +1,88 @@
#
# Simple Test Makefile
# Mike Lam, James Madison University, August 2016
#
# This version of the Makefile includes support for building a test suite. The
# recommended framework is Check (http://check.sourceforge.net/). To build and
# run the test suite, execute the "test" target. The test suite must be located
# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as
# they do in the main Makefile.
#
# To change the default build target (which executes when you just type
# "make"), change the right-hand side of the definition of the "default"
# target.
#
# By default, this makefile will build the project with debugging symbols and
# without optimization. To change this, edit or remove the "-g" and "-O0"
# options in CFLAGS and LDFLAGS accordingly.
#
# By default, this makefile build the application using the GNU C compiler,
# adhering to the C99 standard with all warnings enabled.
# application-specific settings and run target
EXE=../fsm
TEST=testsuite
MODS=public.o
OBJS=../model.o ../effects.o private.o
LIBS=
UTESTOUT=utests.txt
ITESTOUT=itests.txt
SCHECKOUT=style.txt
default: $(TEST)
$(EXE):
make -C ../
test: utest itest style
@echo "========================================"
utest: $(EXE) $(TEST)
@echo "========================================"
@echo " UNIT TESTS"
@./utests.sh | tee $(UTESTOUT)
itest: $(EXE)
@echo "========================================"
@echo " INTEGRATION TESTS"
@./integration.sh | tee $(ITESTOUT)
style: $(EXE)
@echo "========================================"
@echo " CODING STYLE CHECK"
@./style.sh 2>/dev/null >$(SCHECKOUT)
@cat $(SCHECKOUT)
# compiler/linker settings
CC=gcc
CFLAGS=-g -O0 -Wall --std=c99
LDFLAGS=-g -O0
#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments
#CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments
#LDFLAGS+=-L/opt/local/lib
#LDFLAGS=
LIBS+=-lcheck -lm -lpthread
ifeq ($(shell uname -s),Linux)
LIBS+=-lrt -lsubunit
endif
# build targets
$(TEST): $(TEST).o $(MODS) $(OBJS)
$(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS)
%.o: %.c
$(CC) -c $(CFLAGS) $<
clean:
rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle
.PHONY: default clean test unittest inttest
@@ -0,0 +1 @@
015ebe95460b1d6fbf79df280ec3ae5fab362ae8
@@ -0,0 +1 @@
42ae8f1bdf52b57ad234791690bf611379fc8249
@@ -0,0 +1 @@
ebc846cda2557a05e691ef4d7dc7de4ee312cbd1
@@ -0,0 +1 @@
2fd3754d04e83c387269e26d81186b290d299f82
+1
View File
@@ -0,0 +1 @@
17611b03d098cb2d095ddac6dc39c08f66514f4e
@@ -0,0 +1 @@
10b932ebcb7ca5600c9b81652547ea3721854698
@@ -0,0 +1,2 @@
[Init.TOKEN -> Command]
Starting new command: ls
@@ -0,0 +1,4 @@
[Init.TOKEN -> Command]
Starting new command: ls
[Command.NEWLINE -> Term]
Execute ls with arguments { ls, (null) }
@@ -0,0 +1,8 @@
[Init.TOKEN -> Command]
Starting new command: ls
[Command.TOKEN -> Arguments]
Appending -a to the argument list
[Arguments.TOKEN -> Arguments]
Appending -l to the argument list
[Arguments.NEWLINE -> Term]
Execute ls with arguments { ls, -a, -l, (null) }
@@ -0,0 +1,6 @@
[Init.TOKEN -> Command]
Starting new command: ls
[Command.TOKEN -> Arguments]
Appending data to the argument list
[Arguments.NEWLINE -> Term]
Execute ls with arguments { ls, data, (null) }
+9
View File
@@ -0,0 +1,9 @@
[Init.TOKEN -> Command]
Starting new command: ls
[Command.PIPE -> Make_Pipe]
Set up pipe
Execute ls with arguments { ls, (null) }
[Make_Pipe.TOKEN -> Command]
Starting new command: head
[Command.NEWLINE -> Term]
Execute head with arguments { head, (null) }
@@ -0,0 +1,15 @@
[Init.TOKEN -> Command]
Starting new command: ls
[Command.TOKEN -> Arguments]
Appending -l to the argument list
[Arguments.PIPE -> Make_Pipe]
Set up pipe
Execute ls with arguments { ls, -l, (null) }
[Make_Pipe.TOKEN -> Command]
Starting new command: head
[Command.TOKEN -> Arguments]
Appending -n to the argument list
[Arguments.TOKEN -> Arguments]
Appending 1 to the argument list
[Arguments.NEWLINE -> Term]
Execute head with arguments { head, -n, 1, (null) }
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
EXE="../shell"
function run_test {
# parameters
TAG=$1
ARGS=$2
# file paths
OUTPUT=outputs/$TAG.txt
DIFF=outputs/$TAG.diff
EXPECT=expected/$TAG.txt
VALGRND=valgrind/$TAG.txt
# print tag format
PTAG=$(printf '%-30s' "$TAG")
# check for expected text that needs to be fixed
if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then
echo "$PTAG FAIL ($EXPECT not correct)"
return
fi
# compare the file
SHAOUT=$(shasum "$EXPECT" | awk '{print $1}')
SHAEXP=$(cat "expected/.$TAG.sha")
if [ "$SHAOUT" != "$SHAEXP" ] ; then
echo "$PTAG FAIL ($EXPECT not correct)"
return
fi
# run test and compare output to the expected version
$EXE "$ARGS" 2>/dev/null >"$OUTPUT"
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
if [ -s "$DIFF" ]; then
# try alternative solution (if it exists)
EXPECT=expected/$TAG-2.txt
if [ -e "$EXPECT" ]; then
diff -u "$OUTPUT" "$EXPECT" >"$DIFF"
if [ -s "$DIFF" ]; then
echo "$PTAG FAIL (see $DIFF for details)"
else
echo "$PTAG pass"
fi
else
echo "$PTAG FAIL (see $DIFF for details)"
fi
else
echo "$PTAG pass"
fi
# run valgrind
valgrind $EXE $ARGS &>$VALGRND
}
# initialize output folders
mkdir -p outputs
mkdir -p valgrind
rm -f outputs/* valgrind/*
# run individual tests
source itests.include
# check for memory leaks
LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'`
if [ -z "$LEAK" ]; then
echo "No memory leak found."
else
echo "Memory leak(s) found. See files listed below for details."
grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g'
fi
+11
View File
@@ -0,0 +1,11 @@
# list of integration tests
# format: run_test <TAG> <ARGS>
# <TAG> used as the root for all filenames (i.e., "expected/$TAG.txt")
# <ARGS> command-line arguments to test
run_test INTEG_command "ls"
run_test INTEG_execute "ls NL"
run_test INTEG_execute_name "ls data NL"
run_test INTEG_execute_flags "ls -a -l NL"
run_test INTEG_pipe "ls | head NL"
run_test INTEG_pipe_flags "ls -l | head -n 1 NL"
Binary file not shown.
+53
View File
@@ -0,0 +1,53 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <check.h>
#include "../effects.h"
#include "../model.h"
/* nothing but starting a new cmdline */
START_TEST (UNIT_new_cmdline)
{
printf ("\n======================================\n");
printf ("UNIT TEST: new_cmdline\n\n");
fsm_t *fsm = cmdline_init ();
assert (fsm != NULL);
handle_event (fsm, TOKEN);
ck_assert_int_eq (fsm->state, Command);
free (fsm);
}
END_TEST
/* a single cmdline from NEW to TRM */
START_TEST (UNIT_exec_cmd)
{
printf ("\n======================================\n");
printf ("UNIT TEST: exec_cmd\n\n");
fsm_t *fsm = cmdline_init ();
assert (fsm != NULL);
handle_event (fsm, TOKEN);
ck_assert_int_eq (fsm->state, Command);
handle_event (fsm, NEWLINE);
ck_assert_int_eq (fsm->state, Term);
free (fsm);
}
END_TEST
void public_tests (Suite *s)
{
TCase *tc_public = tcase_create ("Public");
tcase_add_test (tc_public, UNIT_new_cmdline);
tcase_add_test (tc_public, UNIT_exec_cmd);
suite_add_tcase (s, tc_public);
}
+53
View File
@@ -0,0 +1,53 @@
#!/bin/bash
STYLE="gnu"
IGNORE=()
FAIL=0
function comp_file {
SRC=$1
SRC_NAME=$2
# file paths
FORMAT=ckstyle/${SRC_NAME}.$STYLE
DIFF=ckstyle/${SRC_NAME}.diff
# run clang-format and compare results
clang-format --style=$STYLE $source > $FORMAT
diff -u $SRC $FORMAT >$DIFF
PTAG=$(printf '%-30s' "$SRC_NAME")
if [ -s $DIFF ]; then
echo "$PTAG FAIL (see $DIFF for details)"
FAIL=1
else
echo "$PTAG pass"
rm $DIFF
fi
rm $FORMAT
}
mkdir -p ckstyle
rm -f ckstyle/*
for source in $(ls ../*.c ../*.h) ; do
SKIP=0
src=$(basename $source)
for ignore in ${IGNORE[*]} ; do
if [ "$src" = "$ignore" ] ; then
SKIP=1
fi
done
if [ $SKIP = 0 ] ; then
comp_file $source $src
fi
done
if [ $FAIL != 0 ] ; then
echo "Code that does not adhere to GNU standards will not be accepted."
echo "You must fix these files before submission."
else
echo "Code correctly adheres to required style."
fi
+34
View File
@@ -0,0 +1,34 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <time.h>
#include <check.h>
extern void public_tests (Suite *s);
extern void private_tests (Suite *s);
Suite * test_suite (void)
{
Suite *s = suite_create ("Default");
public_tests (s);
private_tests (s);
return s;
}
void run_testsuite ()
{
Suite *s = test_suite ();
SRunner *sr = srunner_create (s);
srunner_run_all (sr, CK_NORMAL);
srunner_free (sr);
}
int main (void)
{
srand((unsigned)time(NULL));
run_testsuite ();
return EXIT_SUCCESS;
}
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
OUTPUT=utests.output
./testsuite > "$OUTPUT" 2>/dev/null
if [ ! -s "$OUTPUT" ] ; then
echo "UNIT FAIL (testsuite produced no output)"
rm -f "$OUTPUT"
exit 1
fi
cat "$OUTPUT" | awk '/Failures/,0'
percent=$(cat "$OUTPUT" | grep Failures | cut -d':' -f1)
rm -f "$OUTPUT"
[ "$percent" = "100%" ] && exit 0
echo "(run ./testsuite in tests directory for more information)"
exit 1