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
+22
View File
@@ -0,0 +1,22 @@
# Ignore all compiled files regardless of location
*.o
tests/public.o
tests/testsuite.o
# But not the private.o
!tests/private.o
# Ignore executables for this project
ptrs
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=ptrs
MODS=movies.o
OBJS=
LIBS=
default: $(EXE)
test: $(EXE)
make -C tests test
# compiler/linker settings
CC=gcc
CFLAGS=-g -O0 -Wall -Werror --std=c99
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
+80
View File
@@ -0,0 +1,80 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "movies.h"
int cmdline (int, char **, bool *);
void
usage (void)
{
printf ("Usage: ptrs [-hm]\n");
printf (" Options are:\n");
printf (" -m Parse the CSV data as a movie\n");
printf (" -h Print this help message\n");
printf ("Only one option is allowable\n");
}
int
main (int argc, char **argv)
{
bool run_movie = false;
if (cmdline (argc, argv, &run_movie) != 0)
return EXIT_FAILURE;
if (!run_movie)
{
usage ();
return EXIT_FAILURE;
}
// Split the following comma-separated value (CSV) data into fields
char *data = "The Shawshank Redemption,1994,Drama";
movie_t movie = split_data (data);
printf ("Title: %s\n", movie.title);
printf ("Year: %d\n", movie.year);
printf ("Genre: %s\n", movie.genre);
char *rebuilt = merge_data (movie);
printf ("%s\n", rebuilt);
free (rebuilt);
free (movie.title);
free (movie.genre);
return EXIT_SUCCESS;
}
/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get
the signal numbers to override, the number of overridden signals, and
the order of signals to raise in the full implementation. */
int
cmdline (int argc, char **argv, bool *movies)
{
int option;
if ((option = getopt (argc, argv, "mh")) == -1)
{
usage ();
return -1;
}
switch (option)
{
case 'h':
*movies = false;
return -1;
case 'm':
*movies = true;
break;
default:
printf ("Invalid command-line option: %c\n", optopt);
usage ();
return -1;
}
return 0;
}
+97
View File
@@ -0,0 +1,97 @@
#define _POSIX_C_SOURCE 200809L // needed for strdup extension
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "movies.h"
/* Given a string (possibly read-only) of IMDB movie data
(formatted as "Title,Year,Genre"), create a movie_t object
based on the appropriate fields. */
movie_t
split_data (char *csv)
{
movie_t movie;
char *temp = strdup (csv);
char *token = strtok (temp, ",");
movie.title = strdup (token); // make a copy the first token as the title
char *year_str = strtok (NULL, ",");
char *endptr;
movie.year = strtol (year_str, &endptr, 10);
char *genre = strtok (NULL, ",");
movie.genre = strdup (genre);
free (temp);
return movie;
}
/* Build a dynamically allocated string from a movie_t object as follows:
"Title [Year] - Genre"
*/
char *
merge_data (movie_t movie)
{
// There are a few ways to merge strings in C. One approach is to start with
// a base string on the heap and repeatedly grow and append the string. In
// pseudocode, it might look like this:
//
// str = strdup ("Hello");
// str = realloc (str, ...length...); // grow
// strncat (str, " World", ...length...); // concatenate
// str = realloc (str, ...length...); // grow
// strncat (str, " Again", ...length...); // concatenate
//
// This is the C equivalent of something like this in Java:
//
// str = "Hello";
// str += " World";
// str += " Again";
//
// Another (generally more efficient) way to do this is to use snprintf(),
// for formatting a string. If you are not familiar with snprintf(), you
// should be familiar with its cousin, printf(). Consider the following
// line of code, which prints three string variables to the screen:
//
// printf ("%s %s %s\n", str1, str2, str3);
//
// You do the exact same thing with snprintf(), but add two variables at
// the beginning: the destination buffer (i.e., where do you want the
// final string in memory) and the length:
//
// snprintf (buffer, length, "%s %s %s\n", str1, str2, str3);
//
// The original strings str1, str2, and str3 are unmodified, because their
// contents are copied into the buffer. snprintf() also has a great built-in
// feature that strncat() doesn't: it works with more than just strings!
//
// int num = 42;
// char *str = "My favorite number is ";
// snprintf (message, length, "%s%d\n", str, num);
//
// Just remember that the message/buffer variable must be a pointer to a
// writable portion of memory where you have allocated enough space to store
// the combined string.
// HINT: When doing string manipulation, you should use a variable to keep
// explicit track of the string length. C string lengths are not inherently
// stored anywhere.
size_t len = 1; // always keep track of the null byte
// +10 byte space for yeaar, spaces, brackets, and dash
len += strlen (movie.title) + 10 + strlen (movie.genre);
// Use one of the two techniques above to allocated enough space for the
// merged string. You should NOT rely on any length unless its exact value
// is known. For instance, you can assume that " [Year] - " adds 10 bytes,
// but you cannot assume anything about the length of the genre or title.
// As such, you need to compute those lengths using strlen(). Do not forget
// to account for the null byte.
char *result = calloc (len, sizeof (char));
snprintf (result, len, "%s [%d] - %s", movie.title, movie.year, movie.genre);
return result;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef __CS361_MOVIES_H__
#define __CS361_MOVIES_H__
#include <stdbool.h>
typedef struct movie
{
char *title;
int year;
char *genre;
} movie_t;
movie_t split_data (char *);
char *merge_data (movie_t);
#endif
+89
View File
@@ -0,0 +1,89 @@
#
# 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=../ptrs
TEST=testsuite
MODS=public.o
OBJS=../movies.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"
@./$(TEST) 2>/dev/null >$(UTESTOUT)
@cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g'
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 -pedantic
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 @@
514af63cc957d92dab366709ad95b76ad030e9d9
@@ -0,0 +1 @@
514af63cc957d92dab366709ad95b76ad030e9d9
@@ -0,0 +1,4 @@
Title: The Shawshank Redemption
Year: 1994
Genre: Drama
The Shawshank Redemption [1994] - Drama
@@ -0,0 +1,4 @@
Title: The Shawshank Redemption
Year: 1994
Genre: Drama
The Shawshank Redemption [1994] - Drama
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
EXE="../ptrs"
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
+7
View File
@@ -0,0 +1,7 @@
# 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_movies "-m"
run_test INTEG_movies_inc "-m"
+95
View File
@@ -0,0 +1,95 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <check.h>
#include "../movies.h"
// Test that split_data gets the title copy
START_TEST (UNIT_split_title)
{
char *csv = "Casablanca,1942,Romance";
movie_t movie = split_data (csv);
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
ck_assert_str_eq (movie.title, "Casablanca");
}
END_TEST
// Test that split_data parses numeric values
START_TEST (UNIT_split_nums)
{
char *csv = "Casablanca,1942,Romance";
movie_t movie = split_data (csv);
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
ck_assert_int_eq (movie.year, 1942);
}
END_TEST
// Test that split_data gets the genre copy
START_TEST (UNIT_split_genre)
{
char *csv = "Casablanca,1942,Romance";
movie_t movie = split_data (csv);
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
ck_assert_str_eq (movie.genre, "Romance");
}
END_TEST
// Test that split_data operates correctly on stack-based strings
START_TEST (UNIT_split_stack)
{
char csv[] = "Casablanca,1942,Romance";
movie_t movie = split_data (csv);
ck_assert_str_eq (movie.title, "Casablanca");
ck_assert_int_eq (movie.year, 1942);
ck_assert_str_eq (movie.genre, "Romance");
ck_assert_str_eq (csv, "Casablanca,1942,Romance");
}
END_TEST
// Test that merge_data combines the fields into a string
START_TEST (UNIT_merge_movie)
{
movie_t movie;
movie.title = "The Lord of the Rings: The Fellowship of the Ring";
movie.year = 2001;
movie.genre = "Fantasy";
char *result = merge_data (movie);
ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy");
free (result);
}
END_TEST
// Test that merge_data combines the fields from the heap into a string
START_TEST (UNIT_merge_heap)
{
movie_t *movie = calloc (1, sizeof (movie_t));
movie->title = "The Lord of the Rings: The Fellowship of the Ring";
movie->year = 2001;
movie->genre = "Fantasy";
char *result = merge_data (*movie);
ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy");
free (result);
result = NULL;
ck_assert_str_eq (movie->title, "The Lord of the Rings: The Fellowship of the Ring");
ck_assert_int_eq (movie->year, 2001);
ck_assert_str_eq (movie->genre, "Fantasy");
free (movie);
}
END_TEST
void public_tests (Suite *s)
{
TCase *tc_public = tcase_create ("Public");
tcase_add_test (tc_public, UNIT_split_title);
tcase_add_test (tc_public, UNIT_split_nums);
tcase_add_test (tc_public, UNIT_split_genre);
tcase_add_test (tc_public, UNIT_split_stack);
tcase_add_test (tc_public, UNIT_merge_movie);
tcase_add_test (tc_public, UNIT_merge_heap);
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
+36
View File
@@ -0,0 +1,36 @@
#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);
void run_testsuite (void);
Suite *
test_suite (void)
{
Suite *s = suite_create ("Default");
public_tests (s);
return s;
}
void
run_testsuite (void)
{
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;
}
+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
+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
procs
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=procs
MODS=signals.o child.o
OBJS=
LIBS=
default: $(EXE)
test: $(EXE)
make -C tests test
# compiler/linker settings
CC=gcc
CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic -D_POSIX_SOURCE
LDFLAGS=-g -O0 -pthread
# 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
+39
View File
@@ -0,0 +1,39 @@
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "child.h"
pid_t
run_child (int rc)
{
char *str = "I am the parent";
// TODO: Add code here to create a child process that prints
// "I am the child" and exits with return code passed as the rc
// parameter to this function. The parent must wait for the child
// to finish before printing the child's exit status (consult the
// textbook for this as needed) before printing "I am the parent".
// The parent should return the child's PID from this function.
int status = 0;
pid_t pid = fork ();
if (pid == 0)
{
printf ("I am the child\n");
exit (rc);
}
else
{
waitpid (pid, &status, 0);
}
printf ("Child exited with status %d\n", WEXITSTATUS (status));
printf ("%s\n", str);
return pid;
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef __CS361_CHILD_H__
#define __CS361_CHILD_H__
pid_t run_child (int);
#endif
+87
View File
@@ -0,0 +1,87 @@
/*
* CS 361: Process management lab
*
* Name:
*/
#include <assert.h>
#include <getopt.h>
#include <inttypes.h>
#include <semaphore.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "child.h"
#include "signals.h"
#define SIGNAL_MAX 20
int cmdline (int, char **, int *, char **);
void
usage (void)
{
printf ("Usage: procs [option]\n");
printf (" Options are:\n");
printf (" -e N Create a child that exits with status N\n");
printf (" -s N Override signal number N and run signal handler\n");
printf ("\n Default is to create a child process with normal exit\n");
}
int
main (int argc, char **argv)
{
char *signal_override = NULL;
int rc = 42;
if (cmdline (argc, argv, &rc, &signal_override) < 0)
{
usage ();
return EXIT_FAILURE;
}
// Set up a timeout to kill the process after 5 seconds
struct itimerval timeout;
memset (&timeout, 0, sizeof (timeout));
timeout.it_value.tv_sec = 5;
timeout.it_value.tv_usec = 0;
setitimer (ITIMER_REAL, &timeout, NULL);
if (signal_override == NULL)
run_child (rc);
else
override (signal_override);
return EXIT_SUCCESS;
}
/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get
the portion of the lab to run. */
int
cmdline (int argc, char **argv, int *rc, char **sig)
{
int option;
while ((option = getopt (argc, argv, "e:s:")) != -1)
{
switch (option)
{
case 'e':
*rc = (int)strtol (optarg, NULL, 10);
break;
case 's':
*sig = optarg;
break;
default:
return -1;
}
}
return 0;
}
+110
View File
@@ -0,0 +1,110 @@
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include "signals.h"
static void catch_loop (int);
/* Convert the short signal name (NNN part of SIGNNN) to its number.
Consult /usr/include/asm/signal.h for the number mappings. For
this lab, hard-code the numbers; do not use the numeric constants.
Return -1 if the name isn't one you need to look up. */
int
signal_lookup (char *name)
{
// TODO: Look up the numbers for INT, SEGV, KILL, and TSTP
if (strcmp (name, "INT") == 0)
return 2;
if (strcmp (name, "SEGV") == 0)
return 11;
if (strcmp (name, "KILL") == 0)
return 9;
if (strcmp (name, "TSTP") == 0)
return 20;
return -1;
}
// Entry point for overriding a signal handler in this lab.
void
override (char *signal)
{
int sig = signal_lookup (signal);
if (sig == SIGKILL)
printf ("Received bad signal name: %s\n", signal);
else
catch_loop (sig);
}
// Generic signal handler. This function doesn't use the signal number
// in the provided distribution.
void
sig_handler (int signum __attribute__ ((unused)))
{
// TODO: Complete this signal handler so that it writes the message
// "Breaking out with signal %d\n" with the signal number that was
// passed. Note that you cannot use printf() in a signal handler.
// You must use write(). After writing the message, exit with return
// code 37.
//
// HINT: If you fail integration tests and get a message that the
// binary files are different, you may be writing an extra byte (the
// null byte). You can make sure you avoid this by using strlen()
// instead of trying to hard-code a length.
char buf[128];
snprintf (buf, 128, "Breaking out with signal %d\n", signum);
write (STDOUT_FILENO, buf, strlen (buf));
exit (37);
}
void
catch_loop (int signum)
{
// sigaction(signum)
// TODO: Override the signal handler for the signal number passed.
// Then create a child that runs the following code:
// prctl (PR_SET_PDEATHSIG, SIGKILL);
// printf ("Child has started\n");
// fflush (stdout);
// while (1) ;
struct sigaction sa = { 0 };
sa.sa_handler = sig_handler;
sigaction (signum, &sa, NULL);
pid_t pid = fork ();
if (pid == 0)
{
prctl (PR_SET_PDEATHSIG, SIGKILL);
printf ("Child has started\n");
fflush (stdout);
while (1)
pause ();
}
// HINT: You will NOT call sig_handler() directly here. Instead,
// you must use sigaction() to modify the signal's interrupt handler
// so that sig_handler() will be called later when the signal occurs.
// After starting the child, the parent will call sleep (1) to wait
// for 1 second before sending the signal to the child. The parent
// must wait for the child to run (and exit), then print
// "Child was stopped with status %d\n" with the child process's exit
// status code, retrieved with waitpid(). Note that you will need to
// call WEXITSTATUS() on the retrieved status code.
sleep (1);
int status = 0;
kill (pid, signum);
waitpid (pid, &status, 0);
printf ("Child was stopped with status %d\n", WEXITSTATUS (status));
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef __CS361_SIGNALS_H__
#define __CS361_SIGNALS_H__
void override (char *);
int signal_lookup (char *);
#endif
+87
View File
@@ -0,0 +1,87 @@
#
# 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=../sigs
TEST=testsuite
MODS=public.o
OBJS=../signals.o ../child.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"
@./$(TEST) 2>/dev/null >$(UTESTOUT)
@cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g'
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 -pedantic
LDFLAGS=-g -O0
CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments
LDFLAGS+=-L/opt/local/lib
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
.PHONY: default clean test unittest inttest
@@ -0,0 +1 @@
6037b06b79a54f57617de151d505444f78a23489
+1
View File
@@ -0,0 +1 @@
ebef9658a6b46bd9cfcfeaf732a6d2444fe0b98a
@@ -0,0 +1 @@
145cbab85e3834cf39635245272d5f80e6bd66f2
@@ -0,0 +1 @@
b4ec94fbc4feb8f7ce089341bd6c51e775fce9e1
@@ -0,0 +1 @@
fb52530bd8b6756b4acec57a42391f8b7c9c0e83
+3
View File
@@ -0,0 +1,3 @@
I am the child
Child exited with status 42
I am the parent
+1
View File
@@ -0,0 +1 @@
Received bad signal name: KILL
@@ -0,0 +1,3 @@
Child has started
Breaking out with signal 11
Child was stopped with status 37
@@ -0,0 +1,3 @@
Child has started
Breaking out with signal 2
Child was stopped with status 37
@@ -0,0 +1,3 @@
I am the child
Child exited with status 54
I am the parent
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
EXE="../procs"
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 (expected file is 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
+10
View File
@@ -0,0 +1,10 @@
# 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_child ""
run_test INTEG_status "-e 54"
run_test INTEG_sigint "-s INT"
run_test INTEG_segfault "-s SEGV"
run_test INTEG_kill "-s KILL"
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <signal.h>
#include <check.h>
#include "../signals.h"
START_TEST (UNIT_good_signals)
{
printf ("\n======================================\n");
printf ("UNIT good signals\n");
printf ("This test checks for signal name lookups\n");
char *names[] = { "INT", "KILL", "SEGV", "TSTP" };
int signals[] = { SIGINT, SIGKILL, SIGSEGV, SIGTSTP };
for (int i = 0; i < 4; i++)
{
int sig = signal_lookup (names[i]);
ck_assert_int_eq (signals[i], sig);
}
}
END_TEST
START_TEST (UNIT_bad_signals)
{
printf ("\n======================================\n");
printf ("UNIT bad signals\n");
printf ("This test checks for other signal name lookups\n");
char *names[] = { "HUP", "ALRM", "ILL", "KIL" };
for (int i = 0; i < 4; i++)
{
int sig = signal_lookup (names[i]);
ck_assert_int_eq (sig, -1);
}
}
END_TEST
void public_tests (Suite *s)
{
TCase *tc_public = tcase_create ("Public");
tcase_add_test (tc_public, UNIT_good_signals);
tcase_add_test (tc_public, UNIT_bad_signals);
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;
}
+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
ipc
testsuite
# Ignore test outputs
tests/ckstyle
tests/itests.txt
tests/outputs
tests/style.txt
tests/valgrind
tests/utests.txt
**/.nfs*
**/.vscode
+52
View File
@@ -0,0 +1,52 @@
#
# 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=ipc
MODS=pipe.o
OBJS=
LIBS=-lm
default: $(EXE)
test: $(EXE)
make -C tests test
# compiler/linker settings
CC=gcc
#CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic
CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic
LDFLAGS=-g -O0 -pthread
# 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
+1
View File
@@ -0,0 +1 @@
hello
+1
View File
@@ -0,0 +1 @@
goodbye
+1
View File
@@ -0,0 +1 @@
alohomora
Binary file not shown.
+87
View File
@@ -0,0 +1,87 @@
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include "pipe.h"
int cmdline (int, char **, bool *, char **, char **, size_t *);
void
usage (void)
{
printf ("Usage: ipc [options] EXEC ARG\n");
printf (" Send ARG through a pipe to child process that runs EXEC.\n");
printf (" Option is:\n");
printf (" -s Use posix_spawn() instead of fork() and exec().\n");
}
int
main (int argc, char **argv)
{
bool use_spawn = false;
char *exec = NULL;
char *fname = NULL;
size_t lineno = -1;
if (cmdline (argc, argv, &use_spawn, &exec, &fname, &lineno) < 0)
{
usage ();
return EXIT_FAILURE;
}
// Use fork/exec or spawn to run the executable exec
char *sum = NULL;
if (!use_spawn)
sum = get_result (exec, fname);
else
sum = spawn_result (exec, fname);
if (sum == NULL)
return EXIT_FAILURE;
printf ("%s(%s) = '%s'\n", exec, fname, sum);
// If you get an error here, it's because sum is not dynamically
// allocated correctly.
free (sum);
return EXIT_SUCCESS;
}
/* Helper for parsing the command line arguments. Sets spawn,
filename, and lineno as call-by-reference parameters. Return 0
on success, -1 otherwise. */
int
cmdline (int argc, char **argv, bool *spawn, char **exec, char **filename,
size_t *lineno)
{
int option;
while ((option = getopt (argc, argv, "sh")) != -1)
{
switch (option)
{
case 's':
*spawn = true;
break;
case 'h':
return -1;
break;
default:
return -1;
}
}
if (optind + 2 != argc)
return -1;
*exec = argv[optind];
*filename = argv[optind + 1];
return 0;
}
+118
View File
@@ -0,0 +1,118 @@
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "pipe.h"
/* Creates a child process. If this fails, close the pipe and return -1.
If the child is created, use it to run the executable program on the
filename passed. Send the result back through the pipe. The parent process
should just return the child's PID. Sample call:
int fd[2];
pipe (fd);
pid_t child_pid = create_child (exec, fd, "foo.txt");
*/
pid_t
create_child (char *const exec, int *pipe, char *const filename)
{
char *args[3] = { exec, filename, NULL };
pid_t pid = fork ();
if (pid == 0)
{
close (pipe[0]);
dup2 (pipe[1], STDOUT_FILENO);
close (pipe[1]);
execvp (exec, args);
exit (-1);
}
close (pipe[1]);
return pid;
}
/* Runs the executable program on the input filename. Start by creating a
pipe and calling create_child() to create the child process. If that
fails, return NULL. Otherwise, implement the parent here so that it
reads the result of the process from the pipe and returns the result
without a newline. The returned string must be a dynamically allocated
copy; you can use the constant BUFFER_LENGTH for its size. Be sure to
close the ends of the pipe at appropriate times. Sample call:
char *sum = get_result ("cksum", "data/f1.txt");
// sum is "3015617425 6 data/f1.txt" [without the newline]
*/
char *
get_result (char *const exec, char *const filename)
{
int fd[2];
if (pipe (fd) == -1)
{
return NULL;
}
pid_t pid = create_child (exec, fd, filename);
char *buffer = calloc (BUFFER_LENGTH, sizeof (char));
ssize_t n = read (fd[0], buffer, BUFFER_LENGTH);
buffer[n - 1] = '\0';
close (fd[0]);
waitpid (pid, NULL, 0);
return buffer;
}
/* Re-implementation of the create_child() in pipe.c. Instead of
using the calls to pipe/fork/dup2/exec, combine the latter three
into a call to posix_spawn(). Sample call:
char *sum = spawn_result ("cksum", "data/f1.txt");
// sum is "3015617425 6 data/f1.txt\n" [with the newline]
*/
char *
spawn_result (char *const exec, char *const filename)
{
// Instead of using fork() and exec(), use the posix_spawn functions.
// Note that you will need to create a pipe, initialize a
// posix_spawn_file_actions_t instance, and add close and dup2 actions
// to it so that the child process writes its STDOUT to the pipe.
// After spawning the new process, make sure to call
// posix_spawn_file_actions_destroy() on the actions to prevent
// memory leaks. To avoid looking up the executable in the $PATH,
// you can use posix_spawnp().
// Parent code: read the value back from the pipe into a dynamically
// allocated buffer. Wait for the child to exit, then return the
// buffer. Make sure to close the ends of the pipe in appropriate
// places.
int fd[2];
pipe (fd);
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init (&actions);
posix_spawn_file_actions_addclose (&actions, fd[0]);
posix_spawn_file_actions_adddup2 (&actions, fd[1], STDOUT_FILENO);
posix_spawn_file_actions_addclose (&actions, fd[1]);
pid_t pid = -1;
char *args[3] = { exec, filename, NULL };
posix_spawnp (&pid, exec, &actions, NULL, args, NULL);
posix_spawn_file_actions_destroy (&actions);
close (fd[1]);
char *buffer = calloc (BUFFER_LENGTH, sizeof (char));
ssize_t n = read (fd[0], buffer, BUFFER_LENGTH);
buffer[n - 1] = '\0';
close (fd[0]);
waitpid (pid, NULL, 0);
return buffer;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef __pipe_h__
#define __pipe_h__
#include <unistd.h>
#define BUFFER_LENGTH 100
pid_t create_child (char *const, int *, char *const);
char *get_result (char *const, char *const);
char *spawn_result (char *const, char *const);
#endif
+86
View File
@@ -0,0 +1,86 @@
#
# 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=../ipc
TEST=testsuite
MODS=public.o
OBJS=../pipe.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"
@./$(TEST) 2>/dev/null >$(UTESTOUT)
@cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g'
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=gnu99 -pedantic
LDFLAGS=-g -O0
CFLAGS=
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
+1
View File
@@ -0,0 +1 @@
hello
+1
View File
@@ -0,0 +1 @@
goodbye
+1
View File
@@ -0,0 +1 @@
alohomora
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
85cdeaba8a3d4b24f3471d8c91724de8fc9eb6de
@@ -0,0 +1 @@
e69f8afeb4d967606c11ee01002df44e6c01b910
@@ -0,0 +1 @@
22cdb52c3ef40e84d4213809c7dcd1747efe8ff0
+1
View File
@@ -0,0 +1 @@
3881b07e1676a7fbe53089dfe7b3add8ffed7fb0
@@ -0,0 +1 @@
e69f8afeb4d967606c11ee01002df44e6c01b910
@@ -0,0 +1 @@
e609d97c46fa6bb297cb26adc575edf33978ca5a
+1
View File
@@ -0,0 +1 @@
cksum(../data/f1.txt) = '3015617425 6 ../data/f1.txt'
@@ -0,0 +1 @@
asldkfja(../data/f1.txt) = ''
@@ -0,0 +1 @@
cksum(../data/f4.txt) = ''
+1
View File
@@ -0,0 +1 @@
shasum(../data/f2.txt) = 'e7d9b82b45d5833c9dada13f2379e7b66c823434 ../data/f2.txt'
@@ -0,0 +1 @@
asldkfja(../data/f1.txt) = ''
@@ -0,0 +1 @@
shasum(../data/f5.txt) = ''
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
EXE="../ipc"
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 (expected file is 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_fork "cksum ../data/f1.txt"
run_test INTEG_fork_no_exec "asldkfja ../data/f1.txt"
run_test INTEG_fork_no_file "cksum ../data/f4.txt"
run_test INTEG_spawn "-s shasum ../data/f2.txt"
run_test INTEG_spawn_no_exec "-s asldkfja ../data/f1.txt"
run_test INTEG_spawn_no_file "-s shasum ../data/f5.txt"
Binary file not shown.
+35
View File
@@ -0,0 +1,35 @@
#include <check.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../pipe.h"
// Change this to true if you want to inspect the files produced in
// tests/inputs for debugging
#define DEBUG false
START_TEST (UNIT_result)
{
printf ("\n======================================\n");
printf ("UNIT_result\n");
printf ("Gets cksum of data/f1.txt\n");
char *result = get_result ("cksum", "data/f1.txt");
ck_assert_str_eq (result, "3015617425 6 data/f1.txt");
}
END_TEST
void public_tests (Suite *s)
{
TCase *tc_public = tcase_create ("Public");
tcase_add_test (tc_public, UNIT_result);
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;
}
+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
net
testsuite
# Ignore test outputs
tests/ckstyle
tests/itests.txt
tests/outputs
tests/style.txt
tests/valgrind
tests/utests.txt
**/.nfs*
**/.vscode
+52
View File
@@ -0,0 +1,52 @@
#
# 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=net
MODS=client.o
OBJS=
LIBS=-lm
default: $(EXE)
test: $(EXE)
make -C tests test
# compiler/linker settings
CC=gcc
#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic
CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic
LDFLAGS=-g -O0 -pthread
# 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

Some files were not shown because too many files have changed in this diff Show More