diff options
63 files changed, 5238 insertions, 0 deletions
| diff --git a/pgm1/beetle.c b/pgm1/beetle.c new file mode 100644 index 0000000..6b8a971 --- /dev/null +++ b/pgm1/beetle.c @@ -0,0 +1,140 @@ +#include<stdlib.h> +#include<stdio.h> +#include<math.h> +#include<errno.h> + +/*  + * The purpose of this program is to simulate and record the average lifetimes + * of beetles on cardboard squares, suspended over vats of boiling oil. It takes + * in two parameters, both positive, long integers. The first integer refers + * to the length of one side of the cardboard square, in inches. The second + * refers to the number of beetles (number of times) with which to run this  + * simulation.  + * + * usage: ./beetle int int (./beetle square_size number_of_beetles) + */ + +long double simulateLifetime(long int squareSize, long int beetleCount) { +	/*  +	 * simulateLifetime returns the collective lifetime of the beetles +	 * suspended over the vat of oil. If this function fails to simulate any +	 * beetles, it will return -1.  +	 * +	 * This simulation places a beetle at the center of a cardboard square.  +	 * The cardboard square is represented by a coordinate grid, with the origin +	 * being at the top-left corner, or the "home position". The  +	 * beetle picks a random angle to move in, represented by random(),and moves  +	 * forward 1 inch. This movement takes 1 second. The beetle waits 1 more +	 * second before starting the whole process over again. Once the beetle's +	 * coordinates move off the bounds of the coordinate grid, it "falls into +	 * the vat" and the simulation continues with the next beetle, decreasing  +	 * the count. +	 *  +	 * long int squareSize - represents the size of one side of the cardboard  +	 * square. +	 *  +	 * long int beetleCount - represents the number of beetles to simulate, or  +	 * rather, the number of times to run the simulation. +	 */ +	 +	long double beetleX; +	long double beetleY; +	long double lifetime; +	double angle; +	long int count; +	long int seconds; +	 +	lifetime = -1; +	count = beetleCount; +	seconds = 0; +		 +	while (count > 0) { +		beetleX = squareSize / 2; +		beetleY = squareSize / 2; +		seconds = 0; +		 +		while (beetleX >= 0 && beetleY >= 0 && beetleX <= squareSize  +			&& beetleY <= squareSize) { +			angle = random(); +			beetleX += cos(angle); +			beetleY += sin(angle); +			seconds++; +			 +			if (beetleX >= 0 && beetleY >= 0 && beetleX <= squareSize  +				&& beetleY <= squareSize) { +				seconds++; // the beetle is resting but hasn't fallen off edge +			} +		} +		 +		lifetime += seconds;		 +		count--; +	} + +	return lifetime; +} + +int main(int argc, char *argv[]) { +	/*  +	 * main sets up the beetle program. It reads the user's input and checks it +	 * to make sure that it is valid. It converts the input to values that the +	 * simulation function can use and then calls simulateLifetime(). main then +	 * prints the output to the console. +	 */ +	long double lifetime; +	long int squareSize; +	long int beetleCount; +	char *squareTest; +	char *countTest; +	 +	// Check for improper number of arguments +	if (argc != 3) { +		fprintf(stderr, "beetle: usage: ./beetle int int\n"); +		exit(1); +	}  +	 +	errno = 0; // Reset error status +	squareSize = strtol(argv[1], &squareTest, 10); +	beetleCount = strtol(argv[2], &countTest, 10); +	 +	// Check for invalid input  +	if (squareTest == argv[1] || countTest == argv[2]) {  +		fprintf(stderr, "beetle: usage: ./beetle int int\n"); +		exit(1); +	} +	 +	// Check for negative input +	if (squareSize < 0 || beetleCount < 0) {  +		fprintf(stderr, "beetle: error: input must be positive integers\n"); +		exit(1); +	} +	 +	// Check for overflow/underflow +	if (errno == ERANGE) {  +		fprintf(stderr, "beetle: error: int too large\n"); +        exit(1); +	} +	 +	// If no square to use for simulation then finish +	if (squareSize == 0) { +		fprintf(stderr, "No square given so lifetime is 0\n"); +		exit(0); +	} +	 +	// If no beetles given then finish +	if (beetleCount == 0) { +		fprintf(stderr, "No beetles given, so lifetime is 0\n"); +		exit(0); +	} +	 +	// Run simulation and check return value +	if ((lifetime = simulateLifetime(squareSize, beetleCount)) == -1) {  +		fprintf(stderr, "beetle: error: beetles have achieved immortality!\n"); +		exit(1); +	} +	 +	// Take the average of the beetles' lifetime and print the output.  +	lifetime = lifetime / beetleCount;  +	printf("%li by %li square, %li beetles, mean beetle lifetime is %.1Lf\n",  +		squareSize, squareSize, beetleCount, lifetime); +	exit(0); +} diff --git a/pgm1/makefile b/pgm1/makefile new file mode 100755 index 0000000..7be689d --- /dev/null +++ b/pgm1/makefile @@ -0,0 +1,36 @@ +all: beetle + +beetle: beetle.c +	gcc -Wall -o beetle beetle.c -lm + +debug: beetle.c +	gcc -Wall -o -g beetle beetle.c -lm + +clean:  +	rm -f beetle + +test: +	gcc -Wall -o beetle beetle.c -lm +	 +	# standard test case +	./beetle 10 10000 +	 +	# too many/too few inputs +	-./beetle 10 +	-./beetle 10 10 10  +	 +	# negatives +	-./beetle -10 10000 +	-./beetle 10 -10000 +	 +	# non-number characters +	-./beetle abc 10000 +	-./beetle 10 abc +	 +	# really big numbers +	-./beetle 2147483647 2 +	-./beetle 2 2147483647 +	 +	# overflow +	-./beetle 10 100000000000000000000000000000 +	-./beetle 100000000000000000000000000000 2 diff --git a/pgm2/input.txt b/pgm2/input.txt new file mode 100644 index 0000000..d879fce --- /dev/null +++ b/pgm2/input.txt @@ -0,0 +1,5 @@ +p2test/f2:2:Want to see the and, by implication, there. +p2test/f2:3:THere might work too. +p2test/d1/f1:5:only a three lines have a "the" in them +p2test/d1/f1:7:several other words +p2test/d1/f1:10:another paragraph without a match diff --git a/pgm2/makefile b/pgm2/makefile new file mode 100755 index 0000000..3486352 --- /dev/null +++ b/pgm2/makefile @@ -0,0 +1,31 @@ +all: +  gcc -Wall -o rgpp rgpp.c -lm + +clean: +  rm rgpp + +debug: +  gcc -Wall -g -o rgpp rgpp.c -lm + +test: +  gcc -Wall -o rgpp rgpp.c -lm + +  # a) [6 pts] Straightforward line mode with a banner and line numbers: +  grep -r -H -n -s -I -i the p2test | ./rgpp -l -b -n + +  # b) [7 pts] Straightforward word mode with a banner and line numbers: +  grep -r -H -n -s -I -i the p2test | ./rgpp -w the -b -n + +  # c) [4 pts] Dealing with lots of files/hits: +  grep -r -H -n -s -I -i the /usr/share/gutenprint | ./rgpp -l -b > output + +  # a) [1 pt] Invalid option (must produce error message): +  grep -r -H -n -s -I -i the p2test | ./rgpp -l -b -k + +  # b) [1 pt] Invalid first option (must produce error message): +  grep -r -H -n -s -I -i the p2test | ./rgpp -n -l -b + +  # c) [1 pt] Mismatch problems (no message and garbage out OK): +  grep -r -H -n -s -I -i se p2test | ./rgpp -w the -b -n + +  rm rgpp diff --git a/pgm2/p2test/d1/a.out b/pgm2/p2test/d1/a.outBinary files differ new file mode 100644 index 0000000..c1ea1d0 --- /dev/null +++ b/pgm2/p2test/d1/a.out diff --git a/pgm2/p2test/d1/f1 b/pgm2/p2test/d1/f1 new file mode 100644 index 0000000..3d3d722 --- /dev/null +++ b/pgm2/p2test/d1/f1 @@ -0,0 +1,11 @@ +this file has +lots of +lines +but +only a three lines have a "the" in them + +several other words +are here + +another paragraph without a match +I believe diff --git a/pgm2/p2test/f1 b/pgm2/p2test/f1 new file mode 100644 index 0000000..eeec7c2 --- /dev/null +++ b/pgm2/p2test/f1 @@ -0,0 +1 @@ +this is a one-line file diff --git a/pgm2/p2test/f2 b/pgm2/p2test/f2 new file mode 100644 index 0000000..0e029cc --- /dev/null +++ b/pgm2/p2test/f2 @@ -0,0 +1,3 @@ +Several lines in this one. +Want to see the and, by implication, there. +THere might work too. diff --git a/pgm2/p2test/f3 b/pgm2/p2test/f3 new file mode 100644 index 0000000..abd9f9e --- /dev/null +++ b/pgm2/p2test/f3 @@ -0,0 +1,2 @@ +this is a one-line file +thethea diff --git a/pgm2/p2test/grepout b/pgm2/p2test/grepout new file mode 100644 index 0000000..2f9cec5 --- /dev/null +++ b/pgm2/p2test/grepout @@ -0,0 +1,6 @@ +./f2:2:Want to see the and, by implication, there. +./f2:3:THere might work too. +./d1/f1:5:only a three lines have a "the" in them +./d1/f1:7:several other words +./d1/f1:10:another paragraph without a match +./f3:2:thethea diff --git a/pgm2/rgpp.c b/pgm2/rgpp.c new file mode 100644 index 0000000..6d7b2ee --- /dev/null +++ b/pgm2/rgpp.c @@ -0,0 +1,381 @@ +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +#define TRUE 1 +#define FALSE 0 +#define BUFF_FILE 256 +#define BUFF_LINE 2048 + +struct NODE { +	char filename[BUFF_FILE]; +	char lineNumber[10]; +	char line[BUFF_LINE]; +	struct NODE *next; +}; + +int buildList(struct NODE **listHead) { +	/* +	 * buildList() reads input from stdin (the resulting output of a grep call) + 	 * and generates a linked list of nodes. Each node contains the filename, + 	 * line number, and line of text of the match that grep specified. The + 	 * function returns a hitCount to main() so that the program knows + 	 * whether or not the input (grep's output) was valid. The line number is +	 * not inserted into each node, but is available for testing purposes +	 * should this be necessary. +	 */ +	char filename[BUFF_FILE]; +	char lineNumber[4]; + 	char line[BUFF_LINE]; +	int hitCount = 0; + 	struct NODE *newNode = NULL; + + 	while (fscanf(stdin, "%256[^:]:%4[^:]:%2048[^\n]\n", filename, +			lineNumber, line) == 3) { + 		newNode = (struct NODE *) malloc(sizeof(struct NODE)); + 		strncpy(newNode->filename, filename, BUFF_FILE); + 		strncpy(newNode->line, line, BUFF_LINE); + 		newNode->next = *listHead; + 		*listHead = newNode; +		hitCount++; + 	} + +	return hitCount; +} + +int lineHunter (struct NODE **listHead, int hitCount, +		int lineNumberSwitch, int bannerSwitch) { +	/* +	 * lineHunter() iterates through the nodes built in buildList() + 	 * and looks for lines in the given files that match the lines presented + 	 * in the nodes themselves. If it finds a match, it prints an '*' next + 	 * to the line that it finds. It only opens new files (files that haven't + 	 * been opened before) and then runs through the nodes a second time to + 	 * find matches to the lines being printed. +	 */ +	char filename[BUFF_FILE]; +	char line[BUFF_LINE]; +	int tagBool; +	int lineCount; +	struct NODE *currentNode = *listHead; +	struct NODE *matchNode = NULL; +	FILE *grepFile = NULL; + +	// If args specify banner then print it. +	if (bannerSwitch) { +		printf("\nTHERE ARE %d MATCHING LINES\n", hitCount); +	} + +	// Iterate through filenames +	while (currentNode->next != NULL) { + +		if (!(grepFile = fopen(currentNode->filename, "r"))) { +			fprintf(stderr, "rgpp: error: grep filename couldn't " +				"be opened -- check input.\n"); +			exit(1); +		} + +		strcpy(filename, currentNode->filename); +		printf("\n====================%s\n", currentNode->filename); +		lineCount = 1; + +		// While in the same file print file contents. +		while (fgets(line, BUFF_LINE, grepFile)) { + +			// Strip the newline off of lines pulled from file. +			line[strlen(line) - 1] = '\0'; +			tagBool = FALSE; +			matchNode = *listHead; + +			// Search through nodes to find match to line. +			while (matchNode != NULL) { + +				if (strcmp(currentNode->filename, matchNode->filename) == 0 +					&& strcmp(currentNode->lineNumber, +						matchNode->lineNumber) == 0 +					&& strcmp(line, matchNode->line) == 0) { +					tagBool = TRUE; +				} + +				matchNode = matchNode->next; +			} + +			// Print tag if match was found in line. +			if (tagBool) { +				printf("*  "); +			} +			else { +				printf("   "); +			} + +			// If  args specify line numbering, print them. +			if (lineNumberSwitch) { +				printf("%4d: ", lineCount); +			} + +			// Print the line of text. +			printf("%s\n", line); +			lineCount++; +		} + +		fclose(grepFile); + +		// Search for next new file to open. +		while (strcmp(filename, currentNode->filename) == 0 +				&& currentNode->next != NULL) { +			currentNode = currentNode->next; +		} + +	} + +	return 0; +} + +int wordHunter (struct NODE **listHead, int lineNumberSwitch, +		int bannerSwitch, char *word) { +	/* +	 * wordHunter() iterates through the nodes created in buildList() + 	 * and looks for lines in the given files that match the lines + 	 * specified by the nodes themselves. If a match is found (aka a line + 	 * is determined to contain matches according to grep) then the function + 	 * goes through the line in the node looking for matching words, printing + 	 * them in inverse video. +	 */ +	char *matchWord[BUFF_FILE]; +	char filename[BUFF_FILE]; +	char line[BUFF_LINE]; +	int i; +	int j; +	int k; +	int hitCount = 0; +	int matchBool; +	int lineCount; +	struct NODE *currentNode = *listHead; +	struct NODE *matchNode = NULL; +	FILE *grepFile = NULL; + +	// Print out the banner based on args. +	if (bannerSwitch) { + +	    // Iterate through files to find matches for hitCount +	    while (currentNode != NULL) { +			i = 0; +			j = 0; + +			// This while loop checks the string character by character for +			// matches to the given word. +			while (currentNode->line[i] != '\0') { + +				if (word[j] == '\0') { +					j = 0; +					hitCount++; +				} +				else if (tolower(currentNode->line[i]) == tolower(word[j])) { +					j++; +				} +				else { +					j = 0; +				} + +				i++; +			} + +	        currentNode = currentNode->next; +	    } + +	    printf("\nTHERE ARE %d MATCHING WORDS\n", hitCount); +	} + +	currentNode = *listHead; + +	// Iterate through filenames +	while (currentNode->next != NULL) { + +	    if (!(grepFile = fopen(currentNode->filename, "r"))) { +	        fprintf(stderr, "rgpp: error: grep filename couldn't be " +				"opened -- check input\n"); +	        exit(1); +	    } + +	    strcpy(filename, currentNode->filename); + +		// Print out filename header. +	    printf("\n=====================%s\n", currentNode->filename); +	    lineCount = 1; + +	    // While in the same file, print file contents +	    while (fgets(line, BUFF_LINE, grepFile)) { +	        // Strip the newline off of lines pulled from file. +	        line[strlen(line) - 1] = '\0'; + +	        // If the args specify line numbering, print them. +	        if (lineNumberSwitch) { +	            printf("%4d: ", lineCount); +	        } + +			// Find if line is  a match in nodes. +			matchNode = *listHead; +			matchBool = FALSE; + +			while (matchNode != NULL) { + +				if (strcmp(line, matchNode->line) == 0) { +					matchBool = TRUE; +				} + +				matchNode = matchNode->next; +			} + +			// If the line isn't a match then print the line +			if (!matchBool) { +				printf("%s", line); +			} +			else { +				// Find and highlight the matching words in the node line. +				i = 0; +				j = 0; +				*matchWord = ""; + +				// This while loop checks the string character by character for +				// matches to the given word. If a match is not found, the +				// characters are printed to output. If a match is found, it +				// is highlighted with inverse video and then printed. +				while (line[i] != '\0') { + +					if (word[j] == '\0') { +						printf("\e[7m"); + +						for (k = i - j; k < i; k++) { +							printf("%c", line[k]); +						} + +						printf("\e[0m"); +						j = 0; +					} + +					if (tolower(line[i]) == tolower(word[j])) { +						j++; +					} + +					else { + +						if (j != 0) { + +							for (k = i - j; k <= i; k++) { +								printf("%c", line[k]); +							} + +							j = 0; +						} + +						else { +							printf("%c", line[i]); +						} + +					} + +					i++; +				} + +			} + +			lineCount++; +			printf("\n"); + +		} + +	    fclose(grepFile); + +	    // Search for next file to open. +	    while (strcmp(filename, currentNode->filename) == 0 +	            && currentNode->next != NULL) { +	        currentNode = currentNode->next; +	    } + +	} + +	return 0; +} + +int main(int argc, char *argv[]) { +	/* +	 * main()'s soul purpose is to receive the command line arguments for rgpp + 	 * and then process them and make the necessary function calls to fulfill + 	 * the user's request. It also checks to make sure that return values are + 	 * valid from those functions. +	 */ +	char word[BUFF_LINE]; +	int i = 1; +	int modeSwitch = -1; +	int lineNumberSwitch = 0; +	int bannerSwitch = 0; +	int hitCount = 0; +	struct NODE *list = NULL; + +	// Check for minimum number of required args. +	if (strcmp(argv[1], "-l") != 0 && strcmp(argv[1], "-w") != 0) { +		fprintf(stderr, "rgpp: usage: rgpp [-l | -w word] {-n} {-b}\n"); +		exit(1); +	} + +	// Read args, set variables based on switches and optional word. +	while(argv[i] != NULL) { + +		if (!strcmp(argv[i], "-l")) { +			modeSwitch = 0; +		} +		else if (!strcmp(argv[i], "-w")) { +			modeSwitch = 1; +			strncpy(word, argv[i+1], BUFF_LINE); +			i++; +		} +		else if (!strcmp(argv[i], "-b")) { +			bannerSwitch = 1; +		} +		else if (!strcmp(argv[i], "-n")) { +			lineNumberSwitch = 1; +		} +		else { +			fprintf(stderr, "rgpp: usage: rgpp [-l | -w word] {-n} {-b}\n"); +			exit(1); +		} + +		i++; +	} + +	// Call buildList to read input and build linked list. +	hitCount = buildList(&list); + +	if (hitCount == 0) { +		fprintf(stderr, "rgpp: error: no grep results found -- " +			"check grep input\n"); +		exit(1); +	} + +	if (modeSwitch) { +		if (wordHunter(&list, lineNumberSwitch, bannerSwitch, word) != 0) { +			fprintf(stderr, "rgpp: error: no word matches --" +				" check grep input and word arg\n"); +			exit(1); +		} +	} +	else { +		if (lineHunter(&list, hitCount, lineNumberSwitch, bannerSwitch) != 0) { +			fprintf(stderr, "rgpp: error: no line matches --" +				" check grep input\n"); +			exit(1); +		} +	} + +	// Free allocated memory. +	struct NODE *tmp; + +	while (list != NULL) { +		tmp = list; +		list = list->next; +		free(tmp); +	} + +	exit(0); +} diff --git a/pgm3/accessed.c b/pgm3/accessed.c new file mode 100644 index 0000000..b1007a9 --- /dev/null +++ b/pgm3/accessed.c @@ -0,0 +1,122 @@ +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <time.h> +#include <unistd.h> + +#define FALSE 0 +#define FILE_BUFF 4096 +#define TRUE  1 + +struct NODE { +    char filename[FILE_BUFF]; +    int inode; +    long atime; +    struct NODE *next; +}; + +int makeList(struct NODE **head) { +    /* +     * makeList builds a linked list of nodes representing the filenames +     * given on stdin and their access time. +     */ +    char filename[FILE_BUFF]; +    int inList = FALSE; +    struct NODE *currentNode = NULL; +    struct NODE *newNode = NULL; +    struct stat statData; + +    while (scanf(" %s", filename) == 1) { + +        if (stat(filename, &statData) == 0 && S_ISREG(statData.st_mode)) { +            inList = FALSE; + +            for (currentNode = *head; currentNode != NULL; currentNode = currentNode->next) { + +                if (currentNode->inode == statData.st_ino) { +                    inList = TRUE; +                } + +            } + +            if (!inList) { +                newNode = (struct NODE *) malloc(sizeof(struct NODE)); +                newNode->inode = statData.st_ino; +                strcpy(newNode->filename, filename); +                newNode->atime = (time(NULL) - statData.st_atime) / 86400; +                newNode->next = *head; +                *head = newNode; +            } + +        } + +    } + +    return 0; +} + +int main(int argc, char *argv[]) { +    /* +     * accessed takes in filenames from stdin and one command line argument, +     * "num". It then builds a linked list of the unique, regular files and +     * prints out those files not accessed in "num" days (if num is positive) +     * or prints out those files accesssed within "num" days (if num is +     * negative). +     */ +    int accessedNum = 0; +    struct NODE *currentNode = NULL; +    struct NODE *list = NULL; + +    // Go through args +    if (argc != 2) { +        fprintf(stderr, "accessed: usage: ./accessed [num days]\n"); +        exit(1); +    } + +    accessedNum = strtol(argv[1], NULL, 10); + +    if (errno == EINVAL) { +        fprintf(stderr, "accessed: error: num must be integer type.\n"); +        exit(1); +    } +    else if (errno == ERANGE) { +        fprintf(stderr, "accessed: error: num must be of integer size.\n"); +        exit(1); +    } +    else if (accessedNum == 0) { +        fprintf(stderr, "accessed: error: num must be nonzero integer.\n"); +        exit(1); +    } + +    if (makeList(&list) != 0) { +        fprintf(stderr, "accessed: error: could not build linked list.\n"); +        exit(1); +    } + +    // print out filenames that match requested access time +    for (currentNode = list; currentNode != NULL; currentNode = currentNode->next) { + +        if (accessedNum > 0 && currentNode->atime > accessedNum) { // not within num days +            printf("%s\n", currentNode->filename); +        } + +        if (accessedNum < 0 && currentNode->atime <= (accessedNum * -1)) { // within num days +            printf("%s\n", currentNode->filename); +        } + +    } + +    // free list +    while (currentNode != NULL) { +        currentNode = list; +        list = list->next; +        free(currentNode); +    } + +    exit(0); +} diff --git a/pgm3/pgm3test.d/fileinfo.c b/pgm3/pgm3test.d/fileinfo.c new file mode 100644 index 0000000..eed7902 --- /dev/null +++ b/pgm3/pgm3test.d/fileinfo.c @@ -0,0 +1,30 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <sys/sysmacros.h> +#include <sys/time.h> + +main(argc,argv) +int argc; char *argv[]; +{ + +  struct stat info; + +  if (argc != 2) { +    fprintf(stderr, "fileinfo: usage fileinfo filename\n"); +    exit(1); +  } + +  if (stat(argv[1], &info) != 0) { +    fprintf(stderr,"fileinfo: cannot stat %s:", argv[1]); +    perror(NULL); +  } +  printf("%s: ", argv[1]); +  printf("(device,i_number)=(%d/%d,%ld)", major(info.st_dev), minor(info.st_dev), (long) info.st_ino); +  printf(" last accessed %d seconds ago\n", time(NULL)-info.st_atime); +  if (S_ISREG(info.st_mode)) +    printf("\tdesignated a regular file\n"); +  if (S_ISLNK(info.st_mode)) +    printf("\tdesignated a symlink\n"); +} diff --git a/pgm3/pgm3test.d/link.c b/pgm3/pgm3test.d/link.c new file mode 100644 index 0000000..eed7902 --- /dev/null +++ b/pgm3/pgm3test.d/link.c @@ -0,0 +1,30 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <sys/sysmacros.h> +#include <sys/time.h> + +main(argc,argv) +int argc; char *argv[]; +{ + +  struct stat info; + +  if (argc != 2) { +    fprintf(stderr, "fileinfo: usage fileinfo filename\n"); +    exit(1); +  } + +  if (stat(argv[1], &info) != 0) { +    fprintf(stderr,"fileinfo: cannot stat %s:", argv[1]); +    perror(NULL); +  } +  printf("%s: ", argv[1]); +  printf("(device,i_number)=(%d/%d,%ld)", major(info.st_dev), minor(info.st_dev), (long) info.st_ino); +  printf(" last accessed %d seconds ago\n", time(NULL)-info.st_atime); +  if (S_ISREG(info.st_mode)) +    printf("\tdesignated a regular file\n"); +  if (S_ISLNK(info.st_mode)) +    printf("\tdesignated a symlink\n"); +} diff --git a/pgm3/pgm3test.d/script b/pgm3/pgm3test.d/script new file mode 100644 index 0000000..9bb8248 --- /dev/null +++ b/pgm3/pgm3test.d/script @@ -0,0 +1,24 @@ +#!/bin/sh + +# after doing a +#  cp -a ~kearns/public/415/P3/pgm3test.d . +# run ./script to set access times +# +# ./script should be run before every test to +# reset access times to well-known values + +OLD='2005-01-01' +NEW='yesterday' + +TOUCH=/usr/bin/touch + +#give a.out current access time +$TOUCH  --time=access ./a.out + +# give fileinfo.c access time of Jan 1, 2005 +$TOUCH  --time=access --date=$OLD ./fileinfo.c + +# subdir/subsubdir files given staggered access times +$TOUCH  --time=access --date=$OLD ./subdir/subsubdir/file1 +$TOUCH  --time=access --date=$NEW ./subdir/subsubdir/file2 +$TOUCH  --time=access --date=$NEW ./subdir/subsubdir/file3 diff --git a/pgm3/pgm3test.d/subdir/subfileinfo.c b/pgm3/pgm3test.d/subdir/subfileinfo.c new file mode 100644 index 0000000..eed7902 --- /dev/null +++ b/pgm3/pgm3test.d/subdir/subfileinfo.c @@ -0,0 +1,30 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <sys/sysmacros.h> +#include <sys/time.h> + +main(argc,argv) +int argc; char *argv[]; +{ + +  struct stat info; + +  if (argc != 2) { +    fprintf(stderr, "fileinfo: usage fileinfo filename\n"); +    exit(1); +  } + +  if (stat(argv[1], &info) != 0) { +    fprintf(stderr,"fileinfo: cannot stat %s:", argv[1]); +    perror(NULL); +  } +  printf("%s: ", argv[1]); +  printf("(device,i_number)=(%d/%d,%ld)", major(info.st_dev), minor(info.st_dev), (long) info.st_ino); +  printf(" last accessed %d seconds ago\n", time(NULL)-info.st_atime); +  if (S_ISREG(info.st_mode)) +    printf("\tdesignated a regular file\n"); +  if (S_ISLNK(info.st_mode)) +    printf("\tdesignated a symlink\n"); +} diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file1 b/pgm3/pgm3test.d/subdir/subsubdir/file1Binary files differ new file mode 100644 index 0000000..1bce2be --- /dev/null +++ b/pgm3/pgm3test.d/subdir/subsubdir/file1 diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file2 b/pgm3/pgm3test.d/subdir/subsubdir/file2Binary files differ new file mode 100644 index 0000000..1bce2be --- /dev/null +++ b/pgm3/pgm3test.d/subdir/subsubdir/file2 diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file3 b/pgm3/pgm3test.d/subdir/subsubdir/file3Binary files differ new file mode 100644 index 0000000..1bce2be --- /dev/null +++ b/pgm3/pgm3test.d/subdir/subsubdir/file3 diff --git a/pgm3/pgm3test.d/symlink.c b/pgm3/pgm3test.d/symlink.c new file mode 100644 index 0000000..eed7902 --- /dev/null +++ b/pgm3/pgm3test.d/symlink.c @@ -0,0 +1,30 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <sys/sysmacros.h> +#include <sys/time.h> + +main(argc,argv) +int argc; char *argv[]; +{ + +  struct stat info; + +  if (argc != 2) { +    fprintf(stderr, "fileinfo: usage fileinfo filename\n"); +    exit(1); +  } + +  if (stat(argv[1], &info) != 0) { +    fprintf(stderr,"fileinfo: cannot stat %s:", argv[1]); +    perror(NULL); +  } +  printf("%s: ", argv[1]); +  printf("(device,i_number)=(%d/%d,%ld)", major(info.st_dev), minor(info.st_dev), (long) info.st_ino); +  printf(" last accessed %d seconds ago\n", time(NULL)-info.st_atime); +  if (S_ISREG(info.st_mode)) +    printf("\tdesignated a regular file\n"); +  if (S_ISLNK(info.st_mode)) +    printf("\tdesignated a symlink\n"); +} diff --git a/pgm3/report.c b/pgm3/report.c new file mode 100644 index 0000000..623de6f --- /dev/null +++ b/pgm3/report.c @@ -0,0 +1,232 @@ +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> + +#define FALSE 0 +#define FILE_BUFF 4096 +#define TRUE  1 + +int terminateFlag = 1; // Used with signal handler function. + +void sigHandler(int signum) { +    /* +     * sigHandler() waits for a SIGUSR1 signal. Upon receiving this signal it +     * decrements terminateFlag, lowering it to zero. +     */ + +    if (signum == SIGUSR1) { +        terminateFlag--; +    } + +} + +int main(int argc, char *argv[]) { +    /* +     * report takes in one required argument ("num") and two optional arguments. +     * -k specifies that output should be printed in kilobytes, not bytes +     * -d specifies a delay that should be waited before reading each filename +     * into the totalsize child process. +     * +     * Report takes in filenames on its stdin and then calls four child +     * processes. Two execute accessed, one with num as an arg and one with +     * -num as an arg. report writes its stdin to the stdin of the accessed +     * children. The accessed children then print their output to the stdin +     * of two more children that execute totalsize. These totalsize +     * children run through the filenames they're given and print out the +     * size of those files back into report. This is all done via pipes. +     * +     * report then prints out the results it receives from its children. +     */ +    char byteSum[10]; +    char delayStr[10]; +    char *envar = NULL; +    char filename[FILE_BUFF]; +    char numNeg[11]; +    char thisPID[10]; +    int accessedNum; +    int delay = 0; +    int i = 2; + +    // Go through args. +    if (argc < 2) { +        fprintf(stderr, "usage: ./report [num days] {-k} {-d} [delay]\n"); +        exit(1); +    } + +    // Fetch accessedNum parameter +    accessedNum = strtol(argv[1], NULL, 10); +    errno = 0; + +    if (accessedNum <= 0) { +        fprintf(stderr, "report: num must be positive, nonzero integer.\n"); +        exit(1); +    } +    else if (errno == ERANGE) { +        fprintf(stderr, "report: num must be of integer size.\n"); +        exit(1); +    } +    else if (errno == EINVAL) { +        fprintf(stderr, "report: num must be integer type.\n"); +        exit(1); +    } + +    // Set the second accessed num as negative. +    sprintf(numNeg, "%d", accessedNum * -1); + +    while (argv[i]) { + +        if (strcmp(argv[i], "-k") == 0) { +            setenv("UNITS", "k", 1); +        } +        else if (strcmp(argv[i], "-d") == 0) { +            delay = strtol(argv[i+1], NULL, 10); +            errno = 0; + +            if (delay <= 0) { +                fprintf(stderr, "report: delay must be positive, nonzero integer.\n"); +                exit(1); +            } +            else if (errno == ERANGE) { +                fprintf(stderr, "report: delay must be of integer size.\n"); +                exit(1); +            } +            else if (errno == EINVAL) { +                fprintf(stderr, "report: delay must be integer type.\n"); +                exit(1); +            } + +            sprintf(delayStr, "%d", delay); +            setenv("TSTALL", delayStr, 1); +            i++; +        } +        else { +            fprintf(stderr, "usage: ./report [num days] {-k} {-d} [delay]\n"); +            exit(1); +        } + +        i++; +    } + +    // Set up signal handling. +    sprintf(thisPID, "%d", getpid()); +    setenv("TMOM", thisPID, 1); +    signal(SIGUSR1, sigHandler); + +    // Set up pipes. +    int pipeRtoA1[2]; pipe(pipeRtoA1); +    int pipeRtoA2[2]; pipe(pipeRtoA2); +    int pipeA1toT1[2]; pipe(pipeA1toT1); +    int pipeA2toT2[2]; pipe(pipeA2toT2); +    int pipeT1toR[2]; pipe(pipeT1toR); +    int pipeT2toR[2]; pipe(pipeT2toR); + +    if (fork() == 0) { // accessed 1 +        // Close unneeded pipes ===================== +                                close(pipeRtoA1[1]); +        close(pipeRtoA2[0]);    close(pipeRtoA2[1]); +        close(pipeA1toT1[0]); +        close(pipeA2toT2[0]);   close(pipeA2toT2[1]); +        close(pipeT1toR[0]);    close(pipeT1toR[1]); +        close(pipeT2toR[0]);    close(pipeT2toR[1]); +        // ========================================== + +        // Dup read end to stdin and write end to stdout +        close(0); dup(pipeRtoA1[0]); close(pipeRtoA1[0]); +        close(1); dup(pipeA1toT1[1]); close(pipeA1toT1[1]); +        execl("accessed", "accessed", argv[1], NULL); +    } +    else if (fork() == 0) { // accessed 2 +        // Close unneeded pipes ===================== +        close(pipeRtoA1[0]);    close(pipeRtoA1[1]); +                                close(pipeRtoA2[1]); +        close(pipeA1toT1[0]);   close(pipeA1toT1[1]); +        close(pipeA2toT2[0]); +        close(pipeT1toR[0]);    close(pipeT1toR[1]); +        close(pipeT2toR[0]);    close(pipeT2toR[1]); +        // ========================================== + +        close(0); dup(pipeRtoA2[0]); close(pipeRtoA2[0]); +        close(1); dup(pipeA2toT2[1]); close(pipeA2toT2[1]); +        execl("accessed", "accessed", numNeg, NULL); +    } +    else if (fork() == 0) { // totalsize 1 +        // Close unneeded pipes ===================== +        close(pipeRtoA1[0]);    close(pipeRtoA1[1]); +        close(pipeRtoA2[0]);    close(pipeRtoA2[1]); +                                close(pipeA1toT1[1]); +        close(pipeA2toT2[0]);   close(pipeA2toT2[1]); +        close(pipeT1toR[0]); +        close(pipeT2toR[0]);    close(pipeT2toR[1]); +        // ========================================== + +        close(0); dup(pipeA1toT1[0]); close(pipeA1toT1[0]); +        close(1); dup(pipeT1toR[1]); close(pipeT1toR[1]); +        execl("totalsize", "totalsize", NULL); +    } +    else if (fork() == 0) { // totalsize 2 +        // Close unneeded pipes ===================== +        close(pipeRtoA1[0]);    close(pipeRtoA1[1]); +        close(pipeRtoA2[0]);    close(pipeRtoA2[1]); +        close(pipeA1toT1[0]);   close(pipeA1toT1[1]); +                                close(pipeA2toT2[1]); +        close(pipeT1toR[0]);    close(pipeT1toR[1]); +        close(pipeT2toR[0]); +        // ========================================== + +        close(0); dup(pipeA2toT2[0]); close(pipeA2toT2[0]); +        close(1); dup(pipeT2toR[1]); close(pipeT2toR[1]); +        execl("totalsize", "totalsize", NULL); +    } + +    // Close those pipes which parent report does not need +    close(pipeRtoA1[0]); +    close(pipeRtoA2[0]); +    close(pipeA1toT1[0]);   close(pipeA1toT1[1]); +    close(pipeA2toT2[0]);   close(pipeA2toT2[1]); +                            close(pipeT1toR[1]); +                            close(pipeT2toR[1]); + +    // Write stdin from report to accessed children +    while (scanf("%s", filename) == 1) { +        write(pipeRtoA1[1], strcat(filename, " "), strlen(filename) + 1); +        write(pipeRtoA2[1], strcat(filename, " "), strlen(filename) + 1); +    } + +    // Close remaining pipes. +    close(pipeRtoA1[1]);    close(pipeRtoA2[1]); + +    // Enter non-terminating loop to wait for children +    while (terminateFlag) { +        printf("*"); +        sleep(1); +    } + +    // Print results of first totalsize child +    read(pipeT1toR[0], byteSum, sizeof(byteSum)); close(pipeT1toR[0]); +    sscanf(byteSum, " %s ", byteSum); +    printf("\nA total of %s ", byteSum); + +    if ((envar = getenv("UNITS")) == NULL || !(envar[0] != 'k' || envar[0] != 'K')) { +        printf("bytes "); +    } + +    printf("are in regular files not accessed for %s days.\n----------\n", argv[1]); + +    // Print results of second totalsize child +    read(pipeT2toR[0], byteSum, sizeof(byteSum)); close(pipeT2toR[0]); +    sscanf(byteSum, " %s ", byteSum); +    printf("A total of %s ", byteSum); + +    if ((envar = getenv("UNITS")) == NULL || !(envar[0] != 'k' || envar[0] != 'K')) { +        printf("bytes "); +    } + +    printf("are in regular files accessed within %s days.\n\n", argv[1]); +    exit(0); +} diff --git a/pgm3/totalsize.c b/pgm3/totalsize.c new file mode 100644 index 0000000..c15a72d --- /dev/null +++ b/pgm3/totalsize.c @@ -0,0 +1,126 @@ +#include <errno.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <time.h> +#include <unistd.h> + +#define FALSE 0 +#define FILE_BUFF 4096 +#define TRUE  1 + +struct NODE { +    int inode; +    long size; +    struct NODE *next; +}; + +int main() { +    /* +     * totalsize takes in a list of filenames on its stdin and adds up the +     * total size of those files (using stat) and prints the results as +     * either bytes or kilobytes. It checks the UNITS environment variable to +     * know whether to print in bytes or kilobytes. It checks the TSTALL +     * environment variable for an integer to use as a delay before checking +     * for files. It also checks the TMOM environment variable for the +     * process ID of a parent process to send a SIGUSR1 signal to. +     * +     * totalsize builds a linked list out of the entered filenames and then +     * goes through the unique, regular files and uses stat() to grab +     * information about those files (inode, size). +     */ +    char *envar = NULL; +    char filename[FILE_BUFF]; +    int inList = FALSE; +    int sigPID; +    int sleepTime; +    long long byteSum = 0; +    struct NODE *currentNode = NULL; +    struct NODE *head = NULL; +    struct NODE *newNode = NULL; +    struct stat statData; + +    // Check TSTALL for time delay +    errno = 0; + +    if ((envar = getenv("TSTALL")) != NULL +            && (sleepTime = strtol(envar, NULL, 10)) > 0 +            && errno == 0) { +        sleep(sleepTime); +    } + +    // Take in standard input and build linked list. +    while (scanf(" %s", filename) == 1) { + +        if (stat(filename, &statData) == 0 && S_ISREG(statData.st_mode)) { +            inList = FALSE; + +            for (currentNode = head; currentNode != NULL; currentNode = currentNode->next) { + +                if (currentNode->inode == statData.st_ino) { +                    inList = TRUE; +                } + +            } + +            if (!inList) { +                newNode = (struct NODE *) malloc(sizeof(struct NODE)); +                newNode->inode = statData.st_ino; +                newNode->size = statData.st_size; +                newNode->next = head; +                head = newNode; +            } + +        } + +        // Check TSTALL for time delay +        errno = 0; + +        if ((envar = getenv("TSTALL")) != NULL +                && (sleepTime = strtol(envar, NULL, 10)) > 0 +                && errno == 0) { +            sleep(sleepTime); +        } + +    } + +    // Add up the number of bytes and print out the results +    byteSum = 0; + +    for (currentNode = head; currentNode != NULL; currentNode = currentNode->next) { +        byteSum += currentNode->size; +    } + +    if ((envar = getenv("UNITS")) != NULL +            && (envar[0] == 'k' || envar[0] == 'K') +            && envar[1] == '\0') { +        byteSum /= 1024; +        printf("%lldkB\n", byteSum); +    } +    else { +        printf("%lld\n", byteSum); +    } + +    // Free list +    while (currentNode != NULL) { +        currentNode = head; +        head = head->next; +        free(currentNode); +    } + +    errno = 0; + +    // Send SIGNUSR1 signal to calling parent +    if ((envar = getenv("TMOM")) != NULL +            && (sigPID = strtol(envar, NULL, 10)) > 0 +            && errno == 0) { +        kill(sigPID, SIGUSR1); +    } + +    exit(0); +} diff --git a/pgm4/TTT.c b/pgm4/TTT.c new file mode 100644 index 0000000..373e44b --- /dev/null +++ b/pgm4/TTT.c @@ -0,0 +1,590 @@ +/* TTT.c + * + * I consulted the following pages to increase my understanding of servers that + * use select and to guide my thoughts of how I should construct my own server + * using the select function. + * http://www.binarytides.com/multiple-socket-connections-fdset-select-linux/ + * http://www.lowtek.com/sockets/select.html + * + * Utilizes code from P. Kearns - 1987, 2009 + * + * Adam Carpenter - 2017 + */ + +#include "ttt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> + +int checkEndGame(char *board) { +    int i; +    int tie = TRUE; + +    // Check rows for win. +    if      (board[0] + board[1] + board[2] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[0] + board[1] + board[2] == 'O' + 'O' + 'O') return O_WIN; +    if      (board[3] + board[4] + board[5] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[3] + board[4] + board[5] == 'O' + 'O' + 'O') return O_WIN; +    if      (board[6] + board[7] + board[8] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[6] + board[7] + board[8] == 'O' + 'O' + 'O') return O_WIN; + +    // Check columns for win. +    if      (board[0] + board[3] + board[6] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[0] + board[3] + board[6] == 'O' + 'O' + 'O') return O_WIN; +    if      (board[1] + board[4] + board[7] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[1] + board[4] + board[7] == 'O' + 'O' + 'O') return O_WIN; +    if      (board[2] + board[5] + board[8] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[2] + board[5] + board[8] == 'O' + 'O' + 'O') return O_WIN; + +    // Check diagonals for win. +    if      (board[0] + board[4] + board[8] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[0] + board[4] + board[8] == 'O' + 'O' + 'O') return O_WIN; +    if      (board[2] + board[4] + board[6] == 'X' + 'X' + 'X') return X_WIN; +    else if (board[2] + board[4] + board[6] == 'O' + 'O' + 'O') return O_WIN; + +    // Check for tie win. +    for (i = 0; i < 9; i++) { +        if (board[i] != 'X' && board[i] != 'O') tie = FALSE; +    } + +    if (tie) { +        return TIE_GAME; +    } + +    return 0; +} + +int main() { +    int i; +    char message[MESSAGE_BUFF]; +    char hostname[MAX_HOSTNAME]; +    int streamPort; + +    int querySock; +    int listenSock; +    int newSock; +    int connSockX; +    int connSockO; +    int queuedPlayers[MAX_CLIENTS]; +    socklen_t length; +    struct sockaddr_in *serverAddr, peerAddr; +    struct addrinfo hints, *addrlist; + +    int hits; +    fd_set writeSockets; +    fd_set readSockets; + +    char gameBoard[] = "123456789"; +    char handleX[MESSAGE_BUFF]; +    char handleO[MESSAGE_BUFF]; +    char gameTurn = 'X'; +    int numPlayers = 0; +    int gameOn = FALSE; +    int winner = 0; + +    int qPort = 0; +    socklen_t qLength; +    struct sockaddr_in *qServerAddr, from; +    struct addrinfo qHints, *qAddrlist; +    socklen_t fsize; +    struct timeval timeout; + +    // **** Create SOCK_DGRAM socket to send datagrams to clients **** +    // Create qServerAddr for dgram. +    memset(&qHints, 0, sizeof(qHints)); +    qHints.ai_family = AF_INET; +    qHints.ai_socktype = SOCK_DGRAM; +    qHints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; +    qHints.ai_protocol = 0; +    qHints.ai_canonname = NULL; +    qHints.ai_addr = NULL; +    qHints.ai_next = NULL; + +    // Do getaddrinfo on qHints. +    if (getaddrinfo(NULL, "0", &qHints, &qAddrlist) != 0) { +        fprintf(stderr, "TTT: couldn't getaddrinfo for dgrams.\n"); +        exit(1); +    } + +    // Set server address. +    qServerAddr = (struct sockaddr_in *) qAddrlist->ai_addr; + +    // Create socket. +    if ((querySock = socket(qAddrlist->ai_family, qAddrlist->ai_socktype, 0)) < 0) { +        fprintf(stderr, "TTT: couldn't create socket for dgrams.\n"); +        exit(1); +    } + +    // Bind socket. +    if (bind(querySock, (struct sockaddr *) qServerAddr, sizeof(struct sockaddr_in)) < 0) { +        fprintf(stderr, "TTT: couldn't bind socket for dgrams.\n"); +        exit(1); +    } + +    // Get the socket name for the querySock. +    qLength = sizeof(struct sockaddr_in); +    if (getsockname(querySock, (struct sockaddr *)qServerAddr, &qLength) < 0) { +        fprintf(stderr, "TTT: couldn't get sock name for dgrams.\n"); +        exit(1); +    } + +    qPort = ntohs(qServerAddr->sin_port); +    timeout.tv_sec = 10; +    timeout.tv_usec = 0; +    setsockopt(querySock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &timeout, sizeof(timeout)); +    // ************************************************************* + +    // **** Create SOCK_STREAM socket to listen for connections **** +    // Create serverAddr for stream vc. +    memset(&hints, 0, sizeof(hints)); +    hints.ai_family = AF_INET; +    hints.ai_socktype = SOCK_STREAM; +    hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; +    hints.ai_protocol = 0; +    hints.ai_canonname = NULL; +    hints.ai_addr = NULL; +    hints.ai_next = NULL; + +    if (getaddrinfo(NULL, "0", &hints, &addrlist) != 0) { +        fprintf(stderr, "TTT: couldn't getaddrinfo.\n"); +        exit(1); +    } + +    // Set server address. +    serverAddr = (struct sockaddr_in *) addrlist->ai_addr; + +    // Create socket. +    if ((listenSock = socket(addrlist->ai_family, addrlist->ai_socktype, 0)) < 0) { +        fprintf(stderr, "TTT: couldn't create socket.\n"); +        exit(1); +    } + +    // Bind socket. +    if (bind(listenSock, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in)) < 0) { +        fprintf(stderr, "TTT: couldn't bind socket.\n"); +        exit(1); +    } + +    length = sizeof(struct sockaddr_in); +    if (getsockname(listenSock, (struct sockaddr *)serverAddr, &length) < 0) { +        fprintf(stderr, "TTT: couldn't get sock name.\n"); +        exit(1); +    } + +    // Write ip and port numbers to game connection file. +    FILE *addrFile; + +    if (!(addrFile = fopen("prof.falken", "w"))) { +        fprintf(stderr, "TTT: couldn't open file!\n"); +        exit(1); +    } + +    if (gethostname(hostname, MAX_HOSTNAME) < 0) { +        fprintf(stderr, "TTT: failed to get hostname.\n"); +        exit(1); +    } + +    streamPort = ntohs(serverAddr->sin_port); +    fprintf(addrFile, "%s\n", hostname); +    fprintf(addrFile, "%d\n", streamPort); +    fprintf(addrFile, "%d\n", qPort); +    fclose(addrFile); +    freeaddrinfo(addrlist); + +    listen(listenSock, 6); +    length = sizeof(peerAddr); + +    handleX[0] = '\0'; +    handleO[0] = '\0'; +    connSockX = 0; +    connSockO = 0; + +    for (i = 0; i < MAX_CLIENTS; i++) { +        queuedPlayers[i] = 0; +    } + +    // **** Main server loop **** +    for (;;) { +        // Set up select sockets. +        FD_ZERO(&readSockets); +        FD_SET(listenSock, &readSockets); +        FD_ZERO(&writeSockets); +        FD_SET(querySock, &readSockets); + +        if (connSockX != 0) { +            FD_SET(connSockX, &writeSockets); +            FD_SET(connSockX, &readSockets); +        } + +        if (connSockO != 0) { +            FD_SET(connSockO, &writeSockets); +            FD_SET(connSockO, &readSockets); +        } + +        for (i = 0; i < MAX_CLIENTS; i++) { +            if (queuedPlayers[i] > 0) { +                if (queuedPlayers[i] != 0) { +                    FD_SET(queuedPlayers[i], &writeSockets); +                } +            } +        } + +        // Select sockets that need attention. +        if ((hits = select(FD_SETSIZE, &readSockets, &writeSockets, NULL, NULL)) < 0) { +            fprintf(stderr, "TTT: select failed.\n"); +            exit(1); +        } + +        // Handle query requests. +        if (FD_ISSET(querySock, &readSockets)) { +            fsize = sizeof(from); +            recvfrom(querySock, message, MESSAGE_BUFF, 0, (struct sockaddr *) &from, &fsize); + +            if (gameOn == TRUE) { +                strcpy(message, "Game in session between "); +                strcat(message, handleX); +                strcat(message, " and "); +                strcat(message, handleO); +                strcat(message, "\n"); +            } +            else { +                strcpy(message, "No game in session...\n"); + +                if (queuedPlayers[0] != 0) { +                    strcat(message, "Players waiting to play:\n"); +                    strcat(message, handleX); +                    strcat(message, "\n"); +                } +                else { +                    strcat(message, "No players waiting to play.\n"); +                } +            } + +            strcat(message, "\0"); +            sendto(querySock, message, MESSAGE_BUFF, 0, (struct sockaddr *) &from, sizeof(struct sockaddr_in)); +        } + +        // Handle new incoming connections. +        if (FD_ISSET(listenSock, &readSockets)) { + +            if ((newSock = accept(listenSock, (struct sockaddr *) &peerAddr, &length)) < 0) { +                fprintf(stderr, "TTT: couldn't accept socket.\n"); +                exit(1); +            } + +            // Queue up player. +            for (i = 0; i < MAX_CLIENTS; i++) { +                if (queuedPlayers[i] == 0 && newSock != 0) { +                    queuedPlayers[i] = newSock; +                    newSock = 0; +                } +            } + +            newSock = 0; +        } + +        // **** Game logic **** +        if (numPlayers == 0) { // Get first opponent. + +            if (queuedPlayers[0] != 0) { +                // Pull most recent client to be X +                connSockX = queuedPlayers[0]; + +                for (i = 1; i < MAX_CLIENTS; i++) { +                    queuedPlayers[i - 1] = queuedPlayers[i]; +                } + +                queuedPlayers[MAX_CLIENTS] = 0; +                if (sendMessage(connSockX, HANDLE) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } +                if (getMessage(connSockX, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } +                else { +                    strcpy(handleX, message); +                    numPlayers++; +                } + +            } + +        } +        else if (numPlayers == 1) { // Get second opponent. + +            if (queuedPlayers[0] != 0) { +                // Pull most recent client to be O +                connSockO = queuedPlayers[0]; + +                for (i = 1; i < MAX_CLIENTS; i++) { +                    queuedPlayers[i - 1] = queuedPlayers[i]; +                } + +                queuedPlayers[MAX_CLIENTS] = 0; +                if (sendMessage(connSockO, HANDLE) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } +                if (getMessage(connSockO, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } +                else { +                    strcpy(handleO, message); +                    numPlayers++; +                } + +            } + +        } +        else if (numPlayers == 2 && gameOn == FALSE) { // Set up a new game. + +            if (FD_ISSET(connSockX, &writeSockets) && FD_ISSET(connSockO, &writeSockets)) { +                // If both clients are ready, inform them of opponents and sides. +                strcpy(message, "p X O "); +                strcat(message, handleO); +                if (sendMessage(connSockX, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } +                strcpy(message, "p O X "); +                strcat(message, handleX); +                if (sendMessage(connSockO, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } +                strcpy(gameBoard, "123456789"); +                gameTurn = 'X'; +                gameOn = TRUE; +            } + +        } +        else if (numPlayers == 2 && gameOn == TRUE) { // Continue turn-taking. + +            if (gameTurn == 'X' && FD_ISSET(connSockX, &writeSockets)) { +                strcpy(message, "b "); +                strcat(message, gameBoard); +                if (sendMessage(connSockO, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // send board to O +                if (sendMessage(connSockO, WAIT) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // tell O to wait for X to play +                if (sendMessage(connSockX, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // send board to X +                if (sendMessage(connSockX, MOVE) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // tell X to make a move +                if (getMessage(connSockX, gameBoard)) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // get move from X +                gameTurn = 'O'; + +                // Check for endgame. +                winner = checkEndGame(gameBoard); +                if (winner) { +                    strcpy(message, "b "); +                    strcat(message, gameBoard); +                    if (sendMessage(connSockX, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    if (sendMessage(connSockO, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    message[0] = ENDGAME; +                    message[1] = winner; +                    message[2] = '\0'; +                    if (sendMessage(connSockX, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    if (sendMessage(connSockO, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } + +            } +            else if (gameTurn == 'O' && FD_ISSET(connSockO, &writeSockets)) { +                strcpy(message, "b "); +                strcat(message, gameBoard); +                if (sendMessage(connSockX, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // send board to X +                if (sendMessage(connSockX, WAIT) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // tell X to wait for O to play +                if (sendMessage(connSockO, message) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // send board to O +                if (sendMessage(connSockO, MOVE) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // tell O to make a move +                if (getMessage(connSockO, gameBoard) == -1) { +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } // get move from O +                gameTurn = 'X'; + +                // Check for endgame. +                winner = checkEndGame(gameBoard); +                if (winner) { +                    strcpy(message, "b "); +                    strcat(message, gameBoard); +                    if (sendMessage(connSockX, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    if (sendMessage(connSockO, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    message[0] = ENDGAME; +                    message[1] = winner; +                    message[2] = '\0'; +                    if (sendMessage(connSockX, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    if (sendMessage(connSockO, message) == -1) { +                        numPlayers = 0; +                        handleX[0] = '\0'; +                        handleO[0] = '\0'; +                        connSockX = 0; +                        connSockO = 0; +                        gameOn = FALSE; +                    } +                    numPlayers = 0; +                    handleX[0] = '\0'; +                    handleO[0] = '\0'; +                    connSockX = 0; +                    connSockO = 0; +                    gameOn = FALSE; +                } + +            } + +        } + +    } + +    exit(0); +} diff --git a/pgm4/inet_addrs.d/Makefile b/pgm4/inet_addrs.d/Makefile new file mode 100644 index 0000000..a10866c --- /dev/null +++ b/pgm4/inet_addrs.d/Makefile @@ -0,0 +1,10 @@ +all: gethost getaddrs + +gethost: gethost.c +	gcc -Wall -o gethost gethost.c + +getaddrs: getaddrs.c +	gcc -Wall -o getaddrs getaddrs.c + +clean: +	rm -f getaddrs gethost diff --git a/pgm4/inet_addrs.d/README b/pgm4/inet_addrs.d/README new file mode 100644 index 0000000..fbfb676 --- /dev/null +++ b/pgm4/inet_addrs.d/README @@ -0,0 +1,11 @@ +A pair of simple programs which play with symbolic and IP addresses. + +Invoke as: +  getaddrs  host   service +     host      should be a symbolic host name +     service   should be a service name, port number, or 0 + +  gethost    ipaddr +     ipaddr    should be a dotted decimal IP address + + diff --git a/pgm4/inet_addrs.d/getaddrs.c b/pgm4/inet_addrs.d/getaddrs.c new file mode 100644 index 0000000..f229662 --- /dev/null +++ b/pgm4/inet_addrs.d/getaddrs.c @@ -0,0 +1,171 @@ +// Using getaddrinfo() to get IPv4 address for +// hostname (argv[1]) and service (argv[2]). +// +// The print_XXX() functions are from Stevens +// and Rago. +// +// P. Kearns, February 2009 + + +#include <netdb.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +void +print_family(struct addrinfo *aip) +{ +    printf(" family "); +    switch (aip->ai_family) { +    case AF_INET: +        printf("inet"); +        break; +    case AF_INET6: +        printf("inet6"); +        break; +    case AF_UNIX: +        printf("unix"); +        break; +    case AF_UNSPEC: +        printf("unspecified"); +        break; +    default: +        printf("unknown"); +    } + +} +void +print_type(struct addrinfo *aip) +{ +    printf(" type "); +    switch (aip->ai_socktype) { +    case SOCK_STREAM: +        printf("stream"); +        break; +    case SOCK_DGRAM: +        printf("datagram"); +        break; +    case SOCK_SEQPACKET: +        printf("seqpacket"); +        break; +    case SOCK_RAW: +        printf("raw"); +        break; +    default: +        printf("unknown (%d)", aip->ai_socktype); +    } +} + +void +print_protocol(struct addrinfo *aip) +{ +    printf(" protocol "); +    switch (aip->ai_protocol) { +    case 0: +        printf("default"); +        break; +    case IPPROTO_TCP: +        printf("TCP"); +        break; +    case IPPROTO_UDP: +        printf("UDP"); +        break; +    case IPPROTO_RAW: +        printf("raw"); +        break; +    default: +        printf("unknown (%d)", aip->ai_protocol); +    } +} + +void +print_flags(struct addrinfo *aip) +{ +    printf("flags"); +    if (aip->ai_flags == 0) { +        printf(" 0"); + +    } else { +        if (aip->ai_flags & AI_PASSIVE) +            printf(" passive"); +        if (aip->ai_flags & AI_CANONNAME) +            printf(" canon"); +        if (aip->ai_flags & AI_NUMERICHOST) +            printf(" numhost"); +#if defined(AI_NUMERICSERV) +        if (aip->ai_flags & AI_NUMERICSERV) +            printf(" numserv"); +#endif +#if defined(AI_V4MAPPED) +        if (aip->ai_flags & AI_V4MAPPED) +            printf(" v4mapped"); +#endif +#if defined(AI_ALL) +        if (aip->ai_flags & AI_ALL) +            printf(" all"); +#endif +    } +} + +int main(int argc,char **argv) +{ +  struct addrinfo *addrlist, *aptr, hints; +  struct sockaddr_in *saddrptr; +  const char * p; char addrbuffer[INET_ADDRSTRLEN]; int ecode; + +  if(argc != 3) { +    fprintf(stderr, "usage: getaddrs node service\n"); +    exit(1); +  } + +// Want IPv4 lookup only. + +  memset(&hints, 0, sizeof(hints)); +  hints.ai_family = AF_INET; +  hints.ai_socktype = 0; +  hints.ai_flags = AI_CANONNAME; +  hints.ai_protocol = 0; +  hints.ai_canonname = NULL; +  hints.ai_addr = NULL; +  hints.ai_next = NULL; + +// Do the lookup. + +  ecode = getaddrinfo(argv[1], argv[2], &hints, &addrlist); +  if (ecode != 0) { +    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +// Scan the list of results. + +  for (aptr = addrlist; aptr != NULL; aptr = aptr->ai_next) { +    print_flags(aptr); print_family(aptr); +    print_type(aptr); print_protocol(aptr); + +    if (aptr->ai_family == AF_INET){ +      printf("\tName: %s\n", aptr->ai_canonname?aptr->ai_canonname:"none"); + +      // Get at filled-in sockaddr_in hanging off the addrinfo. + +      saddrptr = (struct sockaddr_in *) aptr->ai_addr; +      p = inet_ntop(AF_INET, &saddrptr->sin_addr.s_addr, addrbuffer, INET_ADDRSTRLEN); + +      if (!p) { +        perror("inet_ntop"); +        exit(1); +      } + +      printf("IP Address: %s\n", p?p:"unknown"); +      printf("Port: %d\n", ntohs(saddrptr->sin_port)); +    } +  } + +  // Give back result structs (not really impt here). + +  freeaddrinfo(addrlist); +  exit(0); +} diff --git a/pgm4/inet_addrs.d/gethost.c b/pgm4/inet_addrs.d/gethost.c new file mode 100644 index 0000000..dcfc7fd --- /dev/null +++ b/pgm4/inet_addrs.d/gethost.c @@ -0,0 +1,57 @@ +// Using getnameinfo() to get host name for a +// given IP address (argv[1]). +// +// P. Kearns, February 2009 + +#include <netdb.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +int main(int argc, char **argv) +{ +   +  int ecode; +  struct in_addr fillin; +  struct sockaddr_in addr; +  char host[NI_MAXHOST]; + +  if (argc != 2) { +    fprintf(stderr,"usage: gethost address\n"); +    exit(1); +  }	 + +// Dotted decimal to binary IP address. + +  ecode = inet_pton(AF_INET, argv[1], &fillin); +  if (ecode == 0) { +        fprintf(stderr,"inet_pton: invalid address\n"); +        exit(1); +  } +  if (ecode < 0) { +	perror("inet_pton"); exit(1); +        exit(1); +  } + +// Fill in blanks of a sockaddr_in after zeroing it out. + +  memset(&addr, 0, sizeof(addr)); +  addr.sin_family = AF_INET; +  addr.sin_port = htons(0); +  memcpy(&(addr.sin_addr), &fillin, sizeof(addr.sin_addr)); + +// Do the lookup. + +  ecode = getnameinfo((struct sockaddr *) &addr, sizeof(addr), +                          host, NI_MAXHOST, NULL, 0, NI_NAMEREQD); +  if (ecode) { +    fprintf(stderr, "getnameinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +  printf("Hostname: %s\n", host);  +  exit(0); +} diff --git a/pgm4/inet_dgrams.d/Makefile b/pgm4/inet_dgrams.d/Makefile new file mode 100644 index 0000000..14018f2 --- /dev/null +++ b/pgm4/inet_dgrams.d/Makefile @@ -0,0 +1,17 @@ +CC=gcc +CFLAGS=-g -Wall + +all: recv_udp send_udp fancy_recv_udp + +recv_udp: recv_udp.o +	$(CC) -o recv_udp recv_udp.o + +fancy_recv_udp: fancy_recv_udp.o +	$(CC) -o fancy_recv_udp fancy_recv_udp.o + +send_udp: send_udp.o +	$(CC) -o send_udp send_udp.o + +clean: +	rm -f *.o recv_udp send_udp fancy_recv_udp + diff --git a/pgm4/inet_dgrams.d/README b/pgm4/inet_dgrams.d/README new file mode 100644 index 0000000..d5bac0f --- /dev/null +++ b/pgm4/inet_dgrams.d/README @@ -0,0 +1,11 @@ +Some simple programs which do datagram-based message passing on the +Internet. + +----------- + +send_udp.c and recv_udp.c are the simple send/receive processes. + +send_udp.c and fancy_recv_udp.c implement the "fancy" receiver that +    was described in lecture. + + diff --git a/pgm4/inet_dgrams.d/fancy_recv_udp.c b/pgm4/inet_dgrams.d/fancy_recv_udp.c new file mode 100644 index 0000000..bb4c20a --- /dev/null +++ b/pgm4/inet_dgrams.d/fancy_recv_udp.c @@ -0,0 +1,117 @@ +/*********************************************************************\ +* 		       FANCY_RECV_UDP.C				      * +* Test of UDP/IP. Receive datagrams on internet socket bound to port  * +* 0x3333 on the local host.					      * +* 								      * +* To use:							      * +* 	1) On the appropriate host, invoke this code by		      * +* 	   "fancy_recv_udp&".					      * +* 	2) Invoke send_udp as many times as desired on the	      * +* 	   remote host.						      * +* 								      * +* Phil Kearns							      * +* April 11, 1987						      * +* 								      * +* Modified: April 10, 1992                                            * +*      responds to input (CR) on stdin by cleanly shutting down       * +*      also shuts down if no datagrams for a minute                   * +\*********************************************************************/ + +#include	<signal.h> +#include	<errno.h> +#include	<string.h> +#include	<stdio.h> +#include	<sys/socket.h> +#include	<netinet/in.h> +#include	<netdb.h> +#include        <arpa/inet.h> +#include        <stdlib.h> + +void printsin(struct sockaddr_in*, char*, char*); + +int main() +{ +  int	socket_fd, cc, hits, ecode; +  socklen_t fsize; +  fd_set mask; +  struct timeval timeout; +  struct sockaddr_in	*s_in, from; +  struct addrinfo hints, *addrlist; +     +  struct { +    char    head; +    u_long  body; +    char    tail; +  } msg; + +  memset(&hints, 0, sizeof(hints)); +  hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; +  hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; hints.ai_protocol = 0; +  hints.ai_canonname = NULL; hints.ai_addr = NULL; +  hints.ai_next = NULL; + +  ecode = getaddrinfo(NULL, "13107", &hints, &addrlist); +  if (ecode != 0) { +    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +  s_in = (struct sockaddr_in *) addrlist->ai_addr; + +  printsin(s_in, "FANCY_RECV_UDP", "Local socket is:"); fflush(stdout); + + +  socket_fd = socket (addrlist->ai_family, addrlist->ai_socktype, 0); +  if (socket_fd < 0) { +    perror ("fancy_recv_udp:socket"); +    exit (1); +  } + +/* +   bind port 0x3333 on the current host to the socket accessed through +   socket_fd. If port in use, die. +*/ +  if (bind(socket_fd, (struct sockaddr *)s_in, sizeof(struct sockaddr_in)) < 0) { +    perror("fancy_recv_udp:bind"); +    exit(1); +  } + +  for(;;) { +    fsize = sizeof(from); +/* Here's the new stuff. Hang a select on the file descriptors 0 (stdin) +   and socket_fd looking to see if either descriptor is able to be read. +   If it's stdin, shut down. If it's socket_fd, proceed as normal. If +   Nothing happens for a minute, shut down also. +*/ +    FD_ZERO(&mask); +    FD_SET(0,&mask); +    FD_SET(socket_fd,&mask); +    timeout.tv_sec = 60; +    timeout.tv_usec = 0; +    if ((hits = select(socket_fd+1, &mask, (fd_set *)0, (fd_set *)0, +                           &timeout)) < 0) { +      perror("fancy_recv_udp:select"); +      exit(1); +    } +    if ( (hits==0) || ((hits>0) && (FD_ISSET(0,&mask))) ) { +      printf("Shutting down\n"); +      exit(0); +    } +    cc = recvfrom(socket_fd,&msg,sizeof(msg),0,(struct sockaddr *)&from,&fsize); +    if (cc < 0) perror("recv_udp:recvfrom"); +    printsin( &from, "recv_udp: ", "Packet from:"); +    printf("Got data ::%c%u%c\n",msg.head,ntohl(msg.body),msg.tail); +    fflush(stdout); +  } +  exit(0); +} + +void printsin(struct sockaddr_in *sin, char *m1, char *m2 ) +{ +  char fromip[INET_ADDRSTRLEN]; + +  printf ("%s %s:\n", m1, m2); +  printf ("  family %d, addr %s, port %d\n", sin -> sin_family, +	    inet_ntop(AF_INET, &(sin->sin_addr.s_addr), fromip, sizeof(fromip)), +            ntohs((unsigned short)(sin -> sin_port))); +} diff --git a/pgm4/inet_dgrams.d/recv_udp.c b/pgm4/inet_dgrams.d/recv_udp.c new file mode 100644 index 0000000..24c47b5 --- /dev/null +++ b/pgm4/inet_dgrams.d/recv_udp.c @@ -0,0 +1,115 @@ +/*********************************************************************\ +* 			  RECV_UDP.C				      * +* Test of UDP/IP. Receive datagrams on internet socket bound to port  * +* 0x3333 on the local host.					      * +* 								      * +* To use:							      * +* 	1) On the appropriate host, invoke this code by		      * +* 	   "recv_udp&".						      * +* 	2) Invoke send_udp as many times as desired on the	      * +* 	   remote host.						      * +* 	3) When done, MAKE SURE TO KILL THIS BACKGROUND PROCESS!      * +* 								      * +* Phil Kearns							      * +* April 11, 1987		 				      * +* 								      * +* Modified February 2009: use getaddrinfo()                           * +\*********************************************************************/ + +#include        <stdlib.h> +#include	<signal.h> +#include	<errno.h> +#include	<string.h> +#include	<stdio.h> +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<netinet/in.h> +#include	<netdb.h> +#include        <arpa/inet.h> + +void printsin(struct sockaddr_in*, char*, char*); + +int main() +{ +    int	socket_fd, cc, ecode; +    socklen_t fsize; +    struct sockaddr_in	*s_in, from; +    struct addrinfo hints, *addrlist; +    void printsin(); + +    struct { +	char	head; +	u_long	body; +	char	tail; +    } msg; + +/* +   In order to attach a name to the socket created above, first fill +   in the appropriate blanks in an inet socket address data structure +   called "s_in". We blindly pick port number 0x3333. The second step +   is to BIND the address to the socket. If port 0x3333 is in use, the +   bind system call will fail detectably. +*/ + +  memset(&hints, 0, sizeof(hints)); +  hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; +  hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; hints.ai_protocol = 0; +  hints.ai_canonname = NULL; hints.ai_addr = NULL; +  hints.ai_next = NULL; + +/* + getaddrinfo() should return a single result, denoting + a SOCK_DGRAM socket on any interface of this system at + port 0x3333. +*/ + +  ecode = getaddrinfo(NULL, "13107", &hints, &addrlist); +  if (ecode != 0) { +    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +  s_in = (struct sockaddr_in *) addrlist->ai_addr; + +  printsin(s_in, "RECV_UDP", "Local socket is:"); fflush(stdout); + +/* +   Create the socket to be used for datagram reception. Initially, +   it has no name in the internet (or any other) domain. +*/ +  socket_fd = socket (addrlist->ai_family, addrlist->ai_socktype, 0); +  if (socket_fd < 0) { +    perror ("recv_udp:socket"); +    exit (1); +  } + +/* +   bind port 0x3333 on the current host to the socket accessed through +   socket_fd. If port in use, bind() will fail and we die. +*/ + +  if (bind(socket_fd, (struct sockaddr *)s_in, sizeof(struct sockaddr_in)) < 0) { +    perror("recv_udp:bind"); +    exit(1); +  } + +  for(;;) { +    fsize = sizeof(from); +    cc = recvfrom(socket_fd, &msg, sizeof(msg), 0, (struct sockaddr *)&from, &fsize); +    if (cc < 0) perror("recv_udp:recvfrom"); +    printsin( &from, "recv_udp: ", "Packet from:"); +    printf("Got data ::%c%u%c\n",msg.head,ntohl(msg.body),msg.tail); +    fflush(stdout); +  } +  exit(0); +} + +void printsin(struct sockaddr_in *sin, char *m1, char *m2 ) +{ +  char fromip[INET_ADDRSTRLEN]; + +  printf ("%s %s:\n", m1, m2); +  printf ("  family %d, addr %s, port %d\n", sin -> sin_family, +	    inet_ntop(AF_INET, &(sin->sin_addr.s_addr), fromip, sizeof(fromip)), +            ntohs((unsigned short)(sin -> sin_port))); +} diff --git a/pgm4/inet_dgrams.d/send_udp.c b/pgm4/inet_dgrams.d/send_udp.c new file mode 100644 index 0000000..63a3c96 --- /dev/null +++ b/pgm4/inet_dgrams.d/send_udp.c @@ -0,0 +1,87 @@ +/***********************************************************************\ +* 			   SEND_UDP.C				        * +* Test of UDP/IP. Send a single dippy datagram to a receiver process    * +* assumed to exist on port 0x3333 on the internet host specified as     * +* the single argument to this program.				        * +* 								        * +* To use:							        * +* 	1) Make sure that recv_udp is running (probably in the	        * +* 	   background on the target host.			        * +* 	2) Issue the command "send_udp xx", where xx is a host name.    * +* 								        * +* Phil Kearns							        * +* April 11, 1987					 	        * +* 								        * +* Modified February 2009: use getaddrinfo()                             * +\***********************************************************************/ + +#include        <stdlib.h> +#include        <unistd.h> +#include	<signal.h> +#include	<errno.h> +#include	<string.h> +#include	<stdio.h> +#include	<sys/types.h> +#include	<sys/socket.h> +#include	<netinet/in.h> +#include	<netdb.h> + +int main(int argc,char **argv) +{ +  int	socket_fd, cc, ecode; +  struct sockaddr_in *dest; +  struct addrinfo hints, *addrlist; +  struct { +    char   head; +    u_long body; +    char   tail; +  } msgbuf; + + +/* +   Use getaddrinfo to create a SOCK_DGRAM sockarddr_in set +   up for the host specified as argv[1] and port 0x3333. +*/ + +  memset(&hints, 0, sizeof(hints)); +  hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; +  hints.ai_flags = AI_NUMERICSERV; hints.ai_protocol = 0; +  hints.ai_canonname = NULL; hints.ai_addr = NULL; +  hints.ai_next = NULL; + +  ecode = getaddrinfo(argv[1], "13107", &hints, &addrlist); +  if (ecode != 0) { +    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +  dest = (struct sockaddr_in *) addrlist->ai_addr; // Will use in sendto(). + +/* +   Set up a datagram (UDP/IP) socket in the Internet domain. +   We will use it as the handle thru which we will send a +   single datagram. Note that, since this no message is ever +   addressed TO this socket, we do not bind an internet address +   to it. It will be assigned a (temporary) address when we send +   a message thru it. +*/ + +  socket_fd = socket (addrlist->ai_family, addrlist->ai_socktype, 0); +  if (socket_fd == -1) { +    perror ("send_udp:socket"); +    exit (1); +  } + +  msgbuf.head = '<'; +  msgbuf.body = htonl(getpid()); +  msgbuf.tail = '>'; + +  cc = sendto(socket_fd,&msgbuf,sizeof(msgbuf),0,(struct sockaddr *) dest, +                  sizeof(struct sockaddr_in)); +  if (cc < 0) { +    perror("send_udp:sendto"); +    exit(1); +  } + +  exit(0); +} diff --git a/pgm4/inet_streams.d/Makefile b/pgm4/inet_streams.d/Makefile new file mode 100644 index 0000000..72c101d --- /dev/null +++ b/pgm4/inet_streams.d/Makefile @@ -0,0 +1,13 @@ +CC=gcc +CFLAGS=-g -Wall + +all: inet_rstream inet_wstream + +inet_rstream: inet_rstream.o +	$(CC) -o inet_rstream inet_rstream.o + +inet_wstream: inet_wstream.o +	$(CC) -o inet_wstream inet_wstream.o + +clean: +	rm -f *.o inet_rstream inet_wstream diff --git a/pgm4/inet_streams.d/README b/pgm4/inet_streams.d/README new file mode 100644 index 0000000..ecbe3dc --- /dev/null +++ b/pgm4/inet_streams.d/README @@ -0,0 +1,3 @@ +A pair of simple programs which interact via a virtual circuit on +the Internet. + diff --git a/pgm4/inet_streams.d/inet_rstream.c b/pgm4/inet_streams.d/inet_rstream.c new file mode 100644 index 0000000..3dafee6 --- /dev/null +++ b/pgm4/inet_streams.d/inet_rstream.c @@ -0,0 +1,110 @@ +/************************************************************************\ +* 			  INET_RSTREAM.C       			         * +* Test of TCP/IP. Set up a socket for establishing a connection at       * +* some free port on the local host. After accepting a connection, speak  * +* briefly to peer, and then read char at a time from the stream. Write   * +* to stdout. At EOF, shut down.                                          * +* 								         * +* Phil Kearns							         * +* April 11, 1987		 				         * +* 								         * +* Modified February 2009 to use getaddrinfo()                            * +\************************************************************************/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +void printsin(struct sockaddr_in*, char*, char*); + +int main() +{ +  int listener;  /* fd for socket on which we get connection requests */ +  int conn;      /* fd for socket thru which we pass data */ +  struct sockaddr_in *localaddr, peer; +  int ecode; +  socklen_t length; +  char ch; +  struct addrinfo hints, *addrlist; + + +/* +   Want to specify local server address of: +      addressing family: AF_INET +      ip address:        any interface on this system +      port:              0 => system will pick free port +*/ + +  memset(&hints, 0, sizeof(hints)); +  hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; +  hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; hints.ai_protocol = 0; +  hints.ai_canonname = NULL; hints.ai_addr = NULL; +  hints.ai_next = NULL; + +  ecode = getaddrinfo(NULL, "0", &hints, &addrlist); +  if (ecode != 0) { +    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +  localaddr = (struct sockaddr_in *) addrlist->ai_addr; + +/* +   Create socket on which we will accept connections. This is NOT the +   same as the socket on which we pass data. +*/ +  if ( (listener = socket( addrlist->ai_family, addrlist->ai_socktype, 0 )) < 0 ) { +    perror("inet_rstream:socket"); +    exit(1); +  } + + +  if (bind(listener, (struct sockaddr *)localaddr, sizeof(struct sockaddr_in)) < 0) { +    perror("inet_rstream:bind"); +    exit(1); +  } + +/* +   Print out the port number assigned to this process by bind(). +*/ +  length = sizeof(struct sockaddr_in); +  if (getsockname(listener, (struct sockaddr *)localaddr, &length) < 0) { +    perror("inet_rstream:getsockname"); +    exit(1); +  } +  printf("RSTREAM:: assigned port number %d\n", ntohs(localaddr->sin_port)); + +/* +   Now accept a single connection. Upon connection, data will be +   passed through the socket on descriptor conn. +*/ +  listen(listener,1); +  length = sizeof(peer); +  if ((conn=accept(listener, (struct sockaddr *)&peer, &length)) < 0) { +    perror("inet_rstream:accept"); +    exit(1); +  } +  printsin(&peer,"RSTREAM::", "accepted connection from"); + +  printf("\n\nRSTREAM:: data from stream:\n"); +  while ( read(conn, &ch, 1) == 1) +    putchar(ch); +  putchar('\n'); +  exit(0); +} + +void printsin(struct sockaddr_in *sin, char *m1, char *m2 ) +{ +  char fromip[INET_ADDRSTRLEN]; + +  printf ("%s %s:\n", m1, m2); +  printf ("  family %d, addr %s, port %d\n", sin -> sin_family, +	    inet_ntop(AF_INET, &(sin->sin_addr.s_addr), fromip, sizeof(fromip)), +            ntohs((unsigned short)(sin -> sin_port))); +} diff --git a/pgm4/inet_streams.d/inet_wstream.c b/pgm4/inet_streams.d/inet_wstream.c new file mode 100644 index 0000000..8dcd284 --- /dev/null +++ b/pgm4/inet_streams.d/inet_wstream.c @@ -0,0 +1,91 @@ +/************************************************************************\ +* 			  INET_WSTREAM.C       			         * +* Test of TCP/IP. Invoke as                                              * +*             inet_wstream hostname portnumber                           * +* in order to send a fragment of an obnoxious Dijkstra quote to a        * +* process assumed to be reading from a stream at the specified           * +* address.                                                               * +* 								         * +* Phil Kearns							         * +* April 11, 1987						         * +* 								         * +* Modified February 2009 to use getaddrinfo()                            * +\************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +char msg[] = { "false pearls before real swine" }; + +int main(argc, argv) +int argc; +char **argv; +{ +  int sock, left, num, put, ecode; +  struct sockaddr_in *server; +  struct addrinfo hints, *addrlist; + +  if (argc != 3) { +    fprintf(stderr,"inet_wstream:usage is inet_wstream host port\n"); +    exit(1); +  } + + + +/* +   Want a sockaddr_in containing the ip address for the system +   specified in argv[1] and the port specified in argv[2]. +*/ + +  memset( &hints, 0, sizeof(hints)); +  hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; +  hints.ai_flags = AI_NUMERICSERV; hints.ai_protocol = 0; +  hints.ai_canonname = NULL; hints.ai_addr = NULL; +  hints.ai_next = NULL; + +  ecode = getaddrinfo(argv[1], argv[2], &hints, &addrlist); +  if (ecode != 0) { +    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ecode)); +    exit(1); +  } + +  server = (struct sockaddr_in *) addrlist->ai_addr; + +/* +   Create the socket. +*/ +  if ( (sock = socket( addrlist->ai_family, addrlist->ai_socktype, 0 )) < 0 ) { +    perror("inet_wstream:socket"); +    exit(1); +  } + +/* +   Connect to data socket on the peer at the specified Internet +   address. +*/ +  if ( connect(sock, (struct sockaddr *)server, sizeof(struct sockaddr_in)) < 0) { +    perror("inet_wstream:connect"); +    exit(1); +  } + +/* +   Write the quote and terminate. This will close the connection on +   this end, so eventually an "EOF" will be detected remotely. +*/ +  left = sizeof(msg); put=0; +  while (left > 0){ +    if((num = write(sock, msg+put, left)) < 0) { +      perror("inet_wstream:write"); +      exit(1); +    } +    else left -= num; +    put += num; +  } +  exit(0); +} diff --git a/pgm4/makefile b/pgm4/makefile new file mode 100644 index 0000000..645618f --- /dev/null +++ b/pgm4/makefile @@ -0,0 +1,19 @@ +all: tttmsg.o ttt.o TTT.o ttt TTT + +tttmsg.o: tttmsg.c +	gcc -Wall -g -c tttmsg.c + +ttt.o: ttt.c +	gcc -Wall -g -c ttt.c + +TTT.o: TTT.c +	gcc -Wall -g -c TTT.c + +ttt: ttt.c +	gcc -Wall -o ttt ttt.o tttmsg.o + +TTT: TTT.c +	gcc -Wall -o TTT TTT.o tttmsg.o + +clean: +	rm -f ttt TTT tttmsg.o ttt.o TTT.o diff --git a/pgm4/ttt.c b/pgm4/ttt.c new file mode 100644 index 0000000..572c588 --- /dev/null +++ b/pgm4/ttt.c @@ -0,0 +1,257 @@ +/* ttt.c + * + * Utilizes code from P. Kearns - 1987, 2009 + * Adam Carpenter - 2017 + */ + +#include "ttt.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +int main(int argc, char *argv[]) { +    int queryFlag; +    struct timeval timeout; +    int i; +    int errno; +    int hits; +    fd_set readSockets; +    // fd_set writeSockets; + +    char message[MESSAGE_BUFF]; +    char playerHandle[MESSAGE_BUFF]; +    char opponentHandle[MESSAGE_BUFF]; +    char playerSide; +    char opponentSide; +    char gameBoard[9]; +    char playerMove; +    int connSock; + +    char hostname[MAX_HOSTNAME]; +    char port[100]; +    struct sockaddr_in *serverAddr; +    struct addrinfo hints, *addrlist; + +    i = 1; +    queryFlag = FALSE; +    timeout.tv_sec = 0; +    timeout.tv_usec = 0; + +    while (argv[i]) { + +        if (strcmp(argv[i], "-q") == 0) { +            queryFlag = TRUE; +        } +        else if (strcmp(argv[i], "-t") == 0) { + +            if (!argv[i + 1]) { +                fprintf(stderr, "usage: ./ttt {-q} {-t} [timeout]\n"); +                exit(1); +            } + +            timeout.tv_sec = strtol(argv[i + 1], NULL, 10); +            errno = 0; + +            if (timeout.tv_sec <= 0) { +                fprintf(stderr, "ttt: timeout must be a positive, nonzero integer.\n"); +                exit(1); +            } +            else if (errno == ERANGE) { +                fprintf(stderr, "ttt: timeout must be of integer size.\n"); +                exit(1); +            } +            else if (errno == EINVAL) { +                fprintf(stderr, "ttt: timeout must be integer type.\n"); +                exit(1); +            } + +            i++; +        } +        else { +            fprintf(stderr, "usage: ./ttt {-q} {-t} [timeout]\n"); +            exit(1); +        } + +        i++; +    } + +    // Get server sockaddr. +    memset( &hints, 0, sizeof(hints)); +    hints.ai_family = AF_INET; + +    if (queryFlag) { +        hints.ai_socktype = SOCK_DGRAM; +    } +    else { +        hints.ai_socktype = SOCK_STREAM; + +    } + +    hints.ai_flags = AI_NUMERICSERV; +    hints.ai_protocol = 0; +    hints.ai_canonname = NULL; +    hints.ai_addr = NULL; +    hints.ai_next = NULL; + +    // (Read name and port from file.) +    FILE *addrfile; + +    if (!(addrfile = fopen("prof.falken", "r"))) { +        sleep(60); +        if (!(addrfile = fopen("prof.falken", "r"))) { +            fprintf(stderr, "ttt: couldn't open file -- is server running?\n"); +            exit(1); +        } +    } + +    fscanf(addrfile, "%s\n", hostname); +    fscanf(addrfile, "%s\n", port); + +    if (queryFlag) { +        fscanf(addrfile, "%s\n", port); +    } + +    fclose(addrfile); + +    if (getaddrinfo(hostname, port, &hints, &addrlist) != 0) { +        fprintf(stderr, "ttt: couldn't getaddrinfo.\n"); +        exit(1); +    } + +    serverAddr = (struct sockaddr_in *) addrlist->ai_addr; + +    if ((connSock = socket(addrlist->ai_family, addrlist->ai_socktype, 0)) < 0) { +        fprintf(stderr, "ttt: couldn't create socket.\n"); +        exit(1); +    } + +    if (queryFlag) { // Wait for query from server if requested. +        printf("Querying server...\n"); +        message[0] = '\0'; +        timeout.tv_sec = 10; +        timeout.tv_usec = 0; +        setsockopt(connSock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &timeout, sizeof(timeout)); +        sendto(connSock, "q\0", strlen("q\0"), 0, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in)); +        recvfrom(connSock, message, MESSAGE_BUFF, 0, NULL, NULL); + +        if (message[0] == '\0') { +            printf("Connection timed out.\n"); +        } +        else { +            printf("%s\n", message); +        } + +        exit(0); +    } +    else { // Else connect to virtual circuit with server. +        if (connect(connSock, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in)) < 0) { +            fprintf(stderr, "ttt: couldn't connect to socket.\n"); +            exit(1); +        } +    } + +    printf("Waiting for server...\n"); +    playerHandle[0] = '\0'; + +    // Enter main client loop. +    for(;;) { +        FD_ZERO(&readSockets); +        FD_SET(connSock, &readSockets); + +        if (timeout.tv_sec == 0) { +            hits = select(FD_SETSIZE, &readSockets, NULL, NULL, NULL); +        } +        else { +            hits = select(FD_SETSIZE, &readSockets, NULL, NULL, &timeout); +        } + +        if (hits < 0) { +            fprintf(stderr, "ttt: failed to select.\n"); +            exit(1); +        } +        else if (hits == 0) { +            fprintf(stderr, "ttt: connection timed out.\n"); +            exit(0); +        } + +        // Get instructions from server. +        getMessage(connSock, message); + +        if (strcmp(message, HANDLE) == 0) { // Get and send handle. + +            if (playerHandle[0] == '\0') { +                printf("Who are you? "); +                scanf("%20[^\n]", playerHandle); +            } + +            sendMessage(connSock, playerHandle); +            printf("Waiting for opponent...\n"); +        } +        else if (strcmp(message, ABORT) == 0) { // Abort. +            printf("Server ordered shutdown. Buhbye!\n"); +            exit(0); +        } +        else if (message[0] == PLAYERID) { // Print name and sides. +            sscanf(message, "p %c %c %[^\n]", &playerSide, &opponentSide, opponentHandle); + +            if (playerSide == 'X') { +                opponentSide = 'O'; +            } +            else { +                opponentSide = 'X'; +            } + +            printf("%s, you are %cs. Your opponent %s is %cs.\n", playerHandle, playerSide, opponentHandle, opponentSide); +            timeout.tv_sec = 0; // Game established -- end timeout. +        } +        else if (message[0] == BOARD) { // Draw the game board. +            sscanf(message, "b %s", gameBoard); +            printf("\n %c | %c | %c\n-----------\n %c | %c | %c\n-----------\n %c | %c | %c\n\n", +                gameBoard[0], gameBoard[1], gameBoard[2], +                gameBoard[3], gameBoard[4], gameBoard[5], +                gameBoard[6], gameBoard[7], gameBoard[8]); +        } +        else if (strcmp(message, WAIT) == 0) { // Wait for opponent. +            printf("Waiting for opponent...\n"); +        } +        else if (strcmp(message, MOVE) == 0) { // Get and send move. +            playerMove = '0'; + +            while (playerMove < '1' ||  playerMove > '9' || gameBoard[playerMove - '0' - 1] != playerMove) { +                if (playerMove != '\n') { +                    printf("Choose an available number on the board.\nYour move: "); +                } + +                playerMove = getchar(); +            } + +            gameBoard[playerMove - '0' - 1] = playerSide; +            strcpy(message, gameBoard); +            sendMessage(connSock, message); +            printf("Waiting for opponent..."); +        } +        else if (message[0] == ENDGAME) { // End Game. + +            if (message[1] == playerSide) { +                printf("\nYou won %s! Congratulations!\n", playerHandle); +            } +            else if (message[1] == opponentSide) { +                printf("\nYou lost! %s won the match.\nBetter luck next time.\n", opponentHandle); +            } +            else { +                printf("\nThe match was a tie!\nBetter luck next time.\n"); +            } +            exit(0); +        } + +    } + +    exit(0); +} diff --git a/pgm4/ttt.h b/pgm4/ttt.h new file mode 100644 index 0000000..abc4c09 --- /dev/null +++ b/pgm4/ttt.h @@ -0,0 +1,33 @@ +/* ttt.h + * + * Adam Carpenter - 2017 + */ + +#ifndef TTT_H +#define TTT_H + + +// Constants. +#define MESSAGE_BUFF 64 +#define MAX_HOSTNAME 255 +#define MAX_CLIENTS 4 +#define FALSE 0 +#define TRUE 1 +#define X_WIN 'X' +#define O_WIN 'O' +#define TIE_GAME '-' + +// Messages that can be sent by the server and client. +#define HANDLE "h" +#define ABORT "a" +#define PLAYERID 'p' +#define BOARD 'b' +#define MOVE "m" +#define WAIT "w" +#define ENDGAME 'e' + +#endif + +// Function declarations. +int sendMessage(int connSock, char *message); +int getMessage(int connSock, char *message); diff --git a/pgm4/tttmsg.c b/pgm4/tttmsg.c new file mode 100644 index 0000000..d5ffb55 --- /dev/null +++ b/pgm4/tttmsg.c @@ -0,0 +1,55 @@ +/* tttmsg.c + * + * Adam Carpenter - 2017 + */ + + +#include "ttt.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int sendMessage(int connSock, char *message) { +    int left, num, put; +    left = MESSAGE_BUFF; +    put = 0; + +    while (left > 0) { + +        if ((num = write(connSock, message + put, left)) < 0) { +            // fprintf(stderr, "ttt/TTT: couldn't send message!\n"); +            perror("ttt/TTT: couldn't send message because "); +            exit(1); +        } +        else if (num == 0) { +            return -1; +        } +        else { +            left -= num; +        } + +        put += num; +    } + +    return 0; +} + +int getMessage(int connSock, char *message) { +    char ch; +    int i = 0; +    int num; + +    for(i = 0; i < MESSAGE_BUFF; i++) { + +        if ((num = read(connSock, &ch, 1)) < 0) { +            fprintf(stderr, "ttt/TTT: couldn't receive message!\n"); +        } +        else if (num == 0) { +            return -1; +        } + +        message[i] = ch; +    } + +    return 0; +} diff --git a/pgm5/counter.c b/pgm5/counter.c new file mode 100644 index 0000000..26735b4 --- /dev/null +++ b/pgm5/counter.c @@ -0,0 +1,690 @@ +/* + * counter.c + * Adam Carpenter - 2017 - acarpent - acarpenter@email.wm.edu + * Utilizes code from P. Kearns + * Utilizes code from linuxthreads demonstration "prodcons.c" + */ + + +// **** Library inclusions **** +#include<errno.h> +#include<pthread.h> +#include<stdio.h> +#include<stdlib.h> +#include<string.h> +#include<time.h> +#include<unistd.h> + + +// **** Definitions for counter **** +#define FALSE           0 +#define LEN_LINE        2048 +#define LEN_FILENAME    4096 +#define MAX_THREADS     26 +#define TRUE            1 +#define USAGE           "counter: usage: counter -b num_lines -t max_counters -d file_delay -D thread_delay [file]...\n" + + +// **** Structure declarations **** +struct prodConBuff { +    /* +     * struct prodConBuff +     */ +     pthread_mutex_t bufferLock;// Mutex ensures exclusive access to buffer. +     int readPos;               // Position for reading. +     int writePos;              // Position for writing. +     pthread_cond_t notEmpty;   // Signaled when buffer isn't empty. +     pthread_cond_t notFull;    // Signaled when buffer isn't full. +    //  char *lines[]; +     char **lines;             // Array of strings serves as data container. +}; + +struct NODE { +    /* +     * struct NODE +     */ +     char *word; // char *word or word[LEN_LINE] +     long count; +     struct NODE *next; +}; + +struct linkedList { +    /* +     * struct linkedList +     */ +     pthread_mutex_t listLock;  // Mutex ensures exclusive access to linked list. +     struct NODE *head;         // Pointer to first node in list. +}; + + +// **** Global variables **** +static char nextThread; +static int fileDelay; +static int maxCounters; +static int moreStuff; +static int numLines; +static int threadDelay; +static pthread_t threadPool[MAX_THREADS]; // Bad Threadpool. +struct prodConBuff buffer; +struct linkedList evenList; +struct linkedList oddList; + + +// **** Structure initializations **** +static void initBuffer(struct prodConBuff *buffer) { +    /* +     * initBuffer() initializes the buffer structure to an empty array ready for +     * producer-consumer actions. +     * The buffer of lines is initialized to NULL. As the program runs, if +     * slots are empty they will be malloc'd to fit given lines being inserted +     * into the buffer, and freed as they are taken out. +     */ +    int i; + +    pthread_mutex_init(&buffer->bufferLock, NULL); +    pthread_cond_init(&buffer->notEmpty, NULL); +    pthread_cond_init(&buffer->notFull, NULL); +    buffer->readPos = 0; +    buffer->writePos = 0; +    buffer->lines = (char **) calloc(numLines, LEN_LINE); + +    for (i = 0; i < numLines; i++) { +        buffer->lines[i] = (char *) calloc(LEN_LINE, sizeof(char)); +    } + +    if (numLines == 1) { +        buffer->lines[0][0] = '\0'; +    } +} + +static void initLinkedList(struct linkedList *list) { +    /* +     * initLinkedList() initializes the linked list structure (its lock and its +     * head node.) +     */ +    pthread_mutex_init(&list->listLock, NULL); +    list->head = NULL; +} + + +// **** Modifier and thread functions **** +static void sleeper(const struct timespec *req, struct timespec *rem) { +    // Researched methods of maintaining nanosleep times online and +    // found an interesting method using recursion that was similar to this. + +    struct timespec newRem; + +    if (nanosleep(req, rem) < 0) { +        sleeper(rem, &newRem); +    } + +} + +static void quitThread() { +    /* +     * quitThread() +     */ +    #ifdef atcdebug +    fprintf(stderr, "counter: thread quit because no more stuff.\n"); +    #endif + +    nextThread--; +    pthread_exit(NULL); +} + +static int putInBuffer(struct prodConBuff *buffer, char *line) { +    /* +     * putInBuffer() +     */ +    int newThread; + +    newThread = FALSE; +    pthread_mutex_lock(&buffer->bufferLock); + +    // If buffer is full, spawn a new counter thread. +    if ((buffer->writePos + 1) % numLines == buffer->readPos) { +        newThread = TRUE; +    } + +    // Wait until buffer isn't full anymore. +    while ((buffer->writePos + 1) % numLines == buffer->readPos) { +        pthread_cond_wait(&buffer->notFull, &buffer->bufferLock); + +        #ifdef atcdebug +        fprintf(stderr, "reader: waiting for space...\n"); +        #endif +    } + +    #ifdef atcdebug +    fprintf(stderr, "reader: gonna insert in slot: %d\n", buffer->writePos); +    #endif + +    // Copy the text into the next slot in the buffer. +    strncpy(buffer->lines[buffer->writePos], line, LEN_LINE); +    buffer->writePos++; + +    if (buffer->writePos >= numLines) { +        buffer->writePos = 0; +    } + +    #ifdef atcdebug +    fprintf(stderr, "reader: buffer is now: {  "); +    int i; +    for (i = 0; i < numLines; i++) { +        if (i == buffer->writePos) { +            fprintf(stderr, "*"); + +        } + +        fprintf(stderr, "%s  ", buffer->lines[i]); + +    } +    fprintf(stderr, "}\n\n"); +    #endif + +    // Signal buffer isn't empty anymore and unlock mutex. +    pthread_cond_signal(&buffer->notEmpty); +    pthread_mutex_unlock(&buffer->bufferLock); +    return newThread; +} + +static int putInSingleBuffer(struct prodConBuff *buffer, char *line) { +    /* +     * putInSingleBuffer(0) +     */ +    int newThread; + +    pthread_mutex_lock(&buffer->bufferLock); +    newThread = FALSE; + +    // If slot is taken, spawn a new counter thread +    if (buffer->lines[0][0] != '\0') { +        newThread = TRUE; +    } + +    // Wait until slot isn't full anymore. +    while (buffer->lines[0][0] != '\0') { +        pthread_cond_wait(&buffer->notFull, &buffer->bufferLock); +    } + +    // Insert line into buffer. +    strncpy(buffer->lines[buffer->writePos], line, LEN_LINE); + +    // Signal buffer slot isn't empty anymore and unlock mutex. +    pthread_cond_signal(&buffer->notEmpty); +    pthread_mutex_unlock(&buffer->bufferLock); +    return newThread; +} + +static void getFromBuffer(struct prodConBuff *buffer, char *line) { +    /* +     * getFromBuffer() +     */ + +    pthread_mutex_lock(&buffer->bufferLock); + +    // If buffer is 'empty' and nothing more will be written, quit. +    if (!moreStuff && buffer->readPos == buffer->writePos) { +        quitThread(); +    } + +    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + +    // Wait until buffer isn't empty anymore. +    while (buffer->readPos == buffer->writePos) { +        #ifdef atcdebug +        fprintf(stderr, "counter: waiting for stuff...\n"); +        #endif + +        pthread_cond_wait(&buffer->notEmpty, &buffer->bufferLock); +    } + +    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + +    #ifdef atcdebug +    fprintf(stderr, "counter: getting from slot: %d\n", buffer->readPos); +    #endif + +    // Get line from buffer and advance read position. +    strncpy(line, buffer->lines[buffer->readPos], LEN_LINE); +    buffer->readPos++; +    if (buffer->readPos >= numLines) buffer->readPos = 0; + +    #ifdef atcdebug +    fprintf(stderr, "counter: buffer is now: {  "); +    int i; +    for (i = 0; i < numLines; i++) { +        if (i == buffer->readPos) { +            fprintf(stderr, "*"); + +        } + +        fprintf(stderr, "%s  ", buffer->lines[i]); + +    } +    fprintf(stderr, "}\n\n"); +    #endif + +    // Signal buffer isn't full anymore and unlock mutex. +    pthread_cond_signal(&buffer->notFull); +    pthread_mutex_unlock(&buffer->bufferLock); +} + +static void getFromSingleBuffer(struct prodConBuff *buffer, char *line) { +    /* +     * getFromSingleBuffer(); +     */ + +    pthread_mutex_lock(&buffer->bufferLock); +    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + +    // Wait until buffer isn't empty anymore. +    while (buffer->lines[0][0] == '\0') { + +        // If buffer slot is empty and nothing more will be written to it, quit. +        if ((buffer->lines[0][0] == 0) && !moreStuff) { +            quitThread(); +        } + +        pthread_cond_wait(&buffer->notEmpty, &buffer->bufferLock); +    } + +    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + +    // Get line from buffer slot. +    strncpy(line, buffer->lines[0], LEN_LINE); +    buffer->lines[0][0] = '\0'; + +    // Signal buffer slot isn't full anmore and unlock mutex. +    pthread_cond_signal(&buffer->notFull); +    pthread_mutex_unlock(&buffer->bufferLock); +} + +static void putInList(struct linkedList *list, char *word) { +    /* +     * putInList() +     */ +    struct NODE *current; +    struct NODE *new; + +    pthread_mutex_lock(&list->listLock); + +    #ifdef atcdebug +    fprintf(stderr, "counter: gonna insert into list\n"); +    #endif + +    if (list->head == NULL) { +        new = (struct NODE *) malloc(sizeof(struct NODE)); +        new->word = (char *) malloc(strlen(word)); +        strcpy(new->word, word); +        new->count = 1; +        list->head = new; +    } +    else { + +        for (current = list->head; current != NULL; current = current->next) { + +            if (strcmp(current->word, word) == 0) { +                // Word already in list; update count. +                current->count++; +                break; +            } +            else if (current->next != NULL +                    && strcmp(word, current->next->word) < 0) { +                // Insert in front of next-greatest lexiographical word. +                new = (struct NODE *) malloc(sizeof(struct NODE)); +                new->word = (char *) malloc(strlen(word)); +                strcpy(new->word, word); +                new->count = 1; +                new->next = current->next; +                current->next = new; +                break; +            } +            else if (current->next == NULL) { +                // Reached the end; Insert at back of list. +                new = (struct NODE *) malloc(sizeof(struct NODE)); +                new->word = (char *) malloc(strlen(word)); +                strcpy(new->word, word); +                new->count = 1; +                new->next = NULL; +                current->next = new; +                break; +            } + +        } + +    } + +    #ifdef atcdebug +    fprintf(stderr, "counter: list is now: {"); +    current = list->head; +    while (current != NULL) { +        fprintf(stderr, "%s->", current->word); +        current = current->next; +    } +    fprintf(stderr, "}\n\n"); +    #endif + +    pthread_mutex_unlock(&list->listLock); +} + +static void printList(struct linkedList *list) { +    /* +     * printList() +     */ +     struct NODE *current; + +     // For debugging purposes and future implementations, lock the list. +     pthread_mutex_lock(&list->listLock); + +     // Print the word of every node in the list. +     for (current = list->head; current != NULL; current = current->next) { +         printf("Count: %ld Word: %s\n", current->count, current->word); +     } + +     pthread_mutex_unlock(&list->listLock); +} + +static void freeList(struct linkedList *list) { +    /* +     * freeList() +     */ +    struct NODE *current; +    struct NODE *temp; + +    // For debugging purposes and future implementations, lock the list. +    pthread_mutex_lock(&list->listLock); + +    // Free every node in the list. +    while (current != NULL) { +        temp = current; +        current = current->next; +        free(temp); +    } + +    pthread_mutex_unlock(&list->listLock); +} + +static void * counterThread(void * arg) { +    /* +     * counterThread() +     */ +    char name; +    char line[LEN_LINE]; +    char *token; +    struct timespec requestedTime; +    struct timespec remainingTime; + +    name = (char) arg; +    requestedTime.tv_sec = threadDelay / 1000; +    requestedTime.tv_nsec = threadDelay % 1000 * 1000000; +    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + +    #ifdef atcdebug +    fprintf(stderr, "counter: name == %c, numLines == %d, threadDelay == %d\n", name, numLines, threadDelay); +    #endif + +    while (TRUE) { + +        // Sleep for allotted time. +        sleeper(&requestedTime, &remainingTime); + +        if (numLines == 1) { +            getFromSingleBuffer(&buffer, line); +        } +        else { +            getFromBuffer(&buffer, line); +        } + +        // Get the first word in the line. +        token = strtok(line, " "); + +        // With each word, see if there are an even or odd number of characters +        // and insert the word into the designated list. Then print the name +        // of the thread on stdout. +        while (token != NULL) { + +            if (strlen(token) % 2 == 0) { +                putInList(&evenList, token); + +                #ifndef atcdebug +                printf("%c ", name); +                #endif + +            } +            else { +                putInList(&oddList, token); + +                #ifndef atcdebug +                printf("%c ", name); +                #endif + +            } + +            token = strtok(NULL, " "); +        } + +    } + +    return NULL; +} + +static void createThread() { +    /* +     * createThread() +     */ + +     // Would a new thread put us over the maximum allowed? +     // If not, create another counter thread as requested. + +     if (((nextThread - 'a') < maxCounters)) { +         #ifdef atcdebug +         fprintf(stderr, "reader: creating thread, name == %c\n", nextThread); +         #endif + +         pthread_create(&threadPool[nextThread - 'a'], NULL, counterThread, (void *) nextThread); +         nextThread++; +     } + +} + + +// **** Main **** +int main(int argc, char *argv[]) { +    /* +     * main() acts as the reader thread, and therefore the producer. +     */ +    char line[LEN_LINE]; +    int i; +    FILE *textFile; +    struct timespec requestedTime; +    struct timespec remainingTime; + +    // Get command line args. +    if (argc < 10) { +        fprintf(stderr, USAGE); +        exit(1); +    } + +    numLines = maxCounters = fileDelay = threadDelay = -1; +    i = 1; +    errno = 0; + +    while (argv[i] && i < 9) { + +        // Get numLines. +		if (strcmp(argv[i], "-b") == 0) { +            i++; + +			if (argv[i]) { +                numLines = strtol(argv[i], NULL, 10); + +                if (errno != 0 || numLines <= 0) { +                    fprintf(stderr, "counter: num_lines must be a positive, non-zero integer.\n"); +                    exit(1); +                } + +            } +            else { +                fprintf(stderr, USAGE); +                exit(1); +            } + +		} +        // Get maxCounters. +		else if (strcmp(argv[i], "-t") == 0) { +            i++; + +            if (argv[i]) { +                maxCounters = strtol(argv[i], NULL, 10); + +                if (errno != 0 || maxCounters <= 0 || maxCounters > 26) { +                    fprintf(stderr, "counter: max_counters must be a positive, non-zero integer no greater than 26.\n"); +                    exit(1); +                } + +            } +            else { +                fprintf(stderr, USAGE); +                exit(1); +            } + +        } +        // Get fileDelay. +        else if (strcmp(argv[i], "-d") == 0) { +            i++; + +            if (argv[i]) { +                fileDelay = strtol(argv[i], NULL, 10); + +                if (errno != 0 || fileDelay < 0) { +                    fprintf(stderr, "counter: file_delay must be a positive integer.\n"); +                    exit(1); +                } + +            } +            else { +                fprintf(stderr, USAGE); +                exit(1); +            } + +        } +        // Get threadDelay. +        else if (strcmp(argv[i], "-D") == 0) { +            i++; + +            if (argv[i]) { +                threadDelay = strtol(argv[i], NULL, 10); + +                if (errno != 0 || threadDelay < 0) { +                    fprintf(stderr, "counter: thread_delay must be a positive integer.\n"); +                    exit(1); +                } + +            } +            else { +                fprintf(stderr, USAGE); +                exit(1); +            } + +        } +        else { + +            if (numLines < 0 || maxCounters < 0 +                    || fileDelay < 0 || threadDelay < 0) { +                fprintf(stderr, USAGE); +                exit(1); +            } + +        } + +        i++; +	} + +    #ifdef atcdebug +    fprintf(stderr, "reader: numLines = %d, maxCounters = %d, fileDelay = %d, " +        "threadDelay = %d\n", numLines, maxCounters, fileDelay, threadDelay); +    #endif + +    // Set up thread environment. +    initBuffer(&buffer); +    initLinkedList(&evenList); +    initLinkedList(&oddList); +    nextThread = 'a'; +    moreStuff = TRUE; + +    // Start up first counter (consumer) thread. +    createThread(); + +    // **** Reader (producer) thread **** +    textFile = NULL; +    requestedTime.tv_sec = threadDelay / 1000; +    requestedTime.tv_nsec = threadDelay % 1000 * 1000000; + +    while (argv[i]) { + +        // Open each file given in argv[]. +        if (!(textFile = fopen(argv[i], "r"))) { +            fprintf(stderr, "counter: could not open %s\n", argv[i]); +        } +        else { + +            // Read each line in the file and insert it into the buffer. +            while (fgets(line, LEN_LINE, textFile)) { +                // Strip newline. +                line[strcspn(line, "\n")] = '\0'; + +                if (numLines == 1) { + +                    // Attempt to create a new thread if function says +                    // buffer was full. +                    if (putInSingleBuffer(&buffer, line) == TRUE) { +                        createThread(); +                    } + +                } +                else { + +                    // Attempt to create a new thread if put function says +                    // buffer was full. +                    if (putInBuffer(&buffer, line) == TRUE) { +                        createThread(); +                    } + +                } + +                // Sleep for allotted time. +                while (nanosleep(&requestedTime, &remainingTime) == -1) { +                    requestedTime = remainingTime; +                } + +            } + +            fclose(textFile); +        } + +        i++; +    } + +    // Let counter threads know that there won't be any more input and wait +    // for threads to finish before printing and terminating. +    moreStuff = FALSE; + +    sleep(2); + +    for (i = 0; i < MAX_THREADS; i++) { + +        if (threadPool[i]) { +            pthread_cancel(threadPool[i]); +        } + +    } + +    printf("\n==== Words with an even number of letters ====\n"); +    printList(&evenList); +    printf("\n==== Words with an odd  number of letters ====\n"); +    printList(&oddList); +    freeList(&evenList); +    freeList(&oddList); +    exit(0); +} diff --git a/pgm5/makefile b/pgm5/makefile new file mode 100644 index 0000000..4de261f --- /dev/null +++ b/pgm5/makefile @@ -0,0 +1,11 @@ +all: counter + +counter: counter.c +	gcc -D_REENTRANT -o counter counter.c -lpthread + +debug: counter.c +	gcc -Wall -g -D_REENTRANT -Datcdebug -o counter counter.c -lpthread + +clean: +	rm -f counter +	gcc -D_REENTRANT -o counter counter.c -lpthread diff --git a/pgm5/p5test/file0 b/pgm5/p5test/file0 new file mode 100644 index 0000000..16d6515 --- /dev/null +++ b/pgm5/p5test/file0 @@ -0,0 +1,17 @@ +a +aa +aaa +aaaa +aaaaa +aaaaaa +aaaaaaa +aaaaaaaa +aaaaaaaaa +aaaaaaaaaa +aaaaaaaaaaa +aaaaaaaaaaaa +aaaaaaaaaaaaa +aaaaaaaaaaaaaa +aaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaa diff --git a/pgm5/p5test/file1 b/pgm5/p5test/file1 new file mode 100644 index 0000000..f9a742d --- /dev/null +++ b/pgm5/p5test/file1 @@ -0,0 +1,2 @@ +a a a a a a +a a a a diff --git a/pgm5/p5test/file2 b/pgm5/p5test/file2 new file mode 100644 index 0000000..0fd6f48 --- /dev/null +++ b/pgm5/p5test/file2 @@ -0,0 +1,3 @@ +aa aa aa  aa  aa  +aa aa aa +aa aa diff --git a/pgm5/p5test/file3 b/pgm5/p5test/file3 new file mode 100644 index 0000000..5234a25 --- /dev/null +++ b/pgm5/p5test/file3 @@ -0,0 +1,9 @@ +aaa aaa aaa aaa				aaa +aaa +aaa + +aaa +aaa +aaa + + diff --git a/pgm5/p5test/file4 b/pgm5/p5test/file4 new file mode 100644 index 0000000..29c981d --- /dev/null +++ b/pgm5/p5test/file4 @@ -0,0 +1,5 @@ +aaaa aaaa +aaaa aaaa +aaaa aaaa +aaaa aaaa +aaaa aaaa diff --git a/pgm5/p5test/file5 b/pgm5/p5test/file5 new file mode 100644 index 0000000..6976d6b --- /dev/null +++ b/pgm5/p5test/file5 @@ -0,0 +1,11 @@ +aaaaa +aaaaa +aaaaa +aaaaa +aaaaa +aaaaa +aaaaa +aaaaa +aaaaa +aaaaa + diff --git a/pgm5/p5test/file6 b/pgm5/p5test/file6 new file mode 100644 index 0000000..abc805a --- /dev/null +++ b/pgm5/p5test/file6 @@ -0,0 +1 @@ +aaaaaa aaaaaa aaaaaa aaaaaa aaaaaa aaaaaa aaaaaa aaaaaa aaaaaa aaaaaa diff --git a/pgm5/prodcons.d/Makefile b/pgm5/prodcons.d/Makefile new file mode 100644 index 0000000..174975d --- /dev/null +++ b/pgm5/prodcons.d/Makefile @@ -0,0 +1,10 @@ +CC=gcc +CFLAGS=-g -Wall -D_REENTRANT +LIBS=-lpthread + +prodcons: prodcons.c +	$(CC) $(CFLAGS) -o prodcons prodcons.c $(LIBS) + +clean: +	rm -f prodcons core.* + diff --git a/pgm5/prodcons.d/README b/pgm5/prodcons.d/README new file mode 100644 index 0000000..3c85ac4 --- /dev/null +++ b/pgm5/prodcons.d/README @@ -0,0 +1,2 @@ +Producer/Consumer with a bounded buffer. From the +linuxthreads demos. diff --git a/pgm5/prodcons.d/prodcons.c b/pgm5/prodcons.d/prodcons.c new file mode 100644 index 0000000..665c744 --- /dev/null +++ b/pgm5/prodcons.d/prodcons.c @@ -0,0 +1,124 @@ +/* The classic producer-consumer example. +   Illustrates mutexes and conditions. +   All integers between 0 and 9999 should be printed exactly twice, +   once to the right of the arrow and once to the left. */ + +#include <stdio.h> +#include <pthread.h> + +#define BUFFER_SIZE 16 + +/* Circular buffer of integers. */ + +struct prodcons +{ +  int buffer[BUFFER_SIZE];	/* the actual data */ +  pthread_mutex_t lock;		/* mutex ensuring exclusive access to buffer */ +  int readpos, writepos;	/* positions for reading and writing */ +  pthread_cond_t notempty;	/* signaled when buffer is not empty */ +  pthread_cond_t notfull;	/* signaled when buffer is not full */ +}; + +/* Initialize a buffer */ +static void +init (struct prodcons *b) +{ +  pthread_mutex_init (&b->lock, NULL); +  pthread_cond_init (&b->notempty, NULL); +  pthread_cond_init (&b->notfull, NULL); +  b->readpos = 0; +  b->writepos = 0; +} + +/* Store an integer in the buffer */ +static void +put (struct prodcons *b, int data) +{ +  pthread_mutex_lock (&b->lock); +  /* Wait until buffer is not full */ +  while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) +    { +      pthread_cond_wait (&b->notfull, &b->lock); +      /* pthread_cond_wait reacquired b->lock before returning */ +    } +  /* Write the data and advance write pointer */ +  b->buffer[b->writepos] = data; +  b->writepos++; +  if (b->writepos >= BUFFER_SIZE) +    b->writepos = 0; +  /* Signal that the buffer is now not empty */ +  pthread_cond_signal (&b->notempty); +  pthread_mutex_unlock (&b->lock); +} + +/* Read and remove an integer from the buffer */ +static int +get (struct prodcons *b) +{ +  int data; +  pthread_mutex_lock (&b->lock); +  /* Wait until buffer is not empty */ +  while (b->writepos == b->readpos) +    { +      pthread_cond_wait (&b->notempty, &b->lock); +    } +  /* Read the data and advance read pointer */ +  data = b->buffer[b->readpos]; +  b->readpos++; +  if (b->readpos >= BUFFER_SIZE) +    b->readpos = 0; +  /* Signal that the buffer is now not full */ +  pthread_cond_signal (&b->notfull); +  pthread_mutex_unlock (&b->lock); +  return data; +} + +/* A test program: one thread inserts integers from 1 to 10000, +   the other reads them and prints them. */ + +#define OVER (-1) + +struct prodcons buffer; + +static void * +producer (void *data) +{ +  int n; +  for (n = 0; n < 10000; n++) +    { +      printf ("%d --->\n", n); +      put (&buffer, n); +    } +  put (&buffer, OVER); +  return NULL; +} + +static void * +consumer (void *data) +{ +  int d; +  while (1) +    { +      d = get (&buffer); +      if (d == OVER) +	break; +      printf ("---> %d\n", d); +    } +  return NULL; +} + +int +main (void) +{ +  pthread_t th_a, th_b; +  void *retval; + +  init (&buffer); +  /* Create the threads */ +  pthread_create (&th_a, NULL, producer, 0); +  pthread_create (&th_b, NULL, consumer, 0); +  /* Wait until producer and consumer finish. */ +  pthread_join (th_a, &retval); +  pthread_join (th_b, &retval); +  return 0; +} diff --git a/pgm5/run.sh b/pgm5/run.sh new file mode 100644 index 0000000..7d75216 --- /dev/null +++ b/pgm5/run.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +make +cp -f counter p5test/counter +cd p5test + +./counter -b 4 -t 1 -d 0 -D 0 file0 +./counter -b 2 -t 2 -d 1000 -D 1000 file0 +./counter -b 10 -t 1 -d 0 -D 0 /usr/share/fortune/linuxcookie +./counter -b 2 -t 26 -d 0 -D 0 /usr/share/fortune/linuxcookie +./counter -b 2 -t 10 -d 0 -D 0 /usr/share/fortune/linuxcookie +./counter -b 2 -t 10 -d 0 -D 0 file{1,2,3,4,5,6} +./counter -b 1 -t 1 -d 0 -D 0 /usr/share/fortune/linuxcookie diff --git a/pgm6/beetle b/pgm6/beetleBinary files differ new file mode 100644 index 0000000..91a4009 --- /dev/null +++ b/pgm6/beetle diff --git a/pgm6/boiler.sh b/pgm6/boiler.sh new file mode 100644 index 0000000..7f8b403 --- /dev/null +++ b/pgm6/boiler.sh @@ -0,0 +1,242 @@ +#!/bin/sh +# +# boiler.sh +# Adam Carpenter - 2017 - acarpent - acarpenter@email.wm.edu +# Utilizes code from P. Kearns - 2004 +# + + +# **** Initializations **** +tempFile="./beetle.temp" +dataFile="./beetle.data" +plotfile="" +lifetimeAwk='BEGIN { FS = " "; } NF == 11 {print $1 " " $11}' +usage="boiler: usage: ./boiler -n num -i min max step {-p plotfile.png} | -r statefile" +trap 'cleanExit' INT +trap 'giveCurrent' USR1 +trap 'dumpState' USR2 + + +# **** Function declarations **** +cleanExit() { +    kill -9 "$pid" 2>/dev/null +    rm -f $tempFile +    echo +    exit 0 +} + +giveCurrent() { +    echo $min +} + +dumpState() { +    kill -9 "$pid" 2>/dev/null +    stateFile="./beetle.state" +    echo $num > $stateFile +    echo $min >> $stateFile +    echo $max >> $stateFile +    echo $step >> $stateFile +    echo $plotFile >> $stateFile +    exec 3<$tempFile + +    while read -u 3 line; do +        echo "$line" >> $stateFile +    done + +    exec 3<&- +    rm -f $tempFile +    echo +    exit 0 +} + +beetlejuice() { +    # beetlejuice +    # beetlejuice +    if [ -f ./beetle ]; then +        ./beetle $min $num >> $tempFile & +    else +        /home/f85/kearns/public/415/p6/beetle $min $num >> $tempFile & +    fi + +    pid=$! + +    while ps -p $pid --format pid= > /dev/null; do +        : # Just waiting for traps. +    done + +    min=`expr $min + $step` +} + +# **** Main script **** +# Remove any residual temp file (should be a nonissue). +rm -f $tempFile +rm -f $dataFile + +# Make sure there are arguments. +if [ -z $1 ]; then +    echo $usage +    exit 1 +fi + +if [ $1 = "-r" ]; then # Operating in restore mode. +    shift + +    if [ ! -e $1 ]; then +        echo boiler: state file not found +        exit 1 +    fi + +    # Get state file and read in beetle arguments. +    if [ ! -z $1 ]; then +        stateFile=$1 +        exec 3<$stateFile +        read -u 3 line +        num=$line +        read -u 3 line +        min=$line +        read -u 3 line +        max=$line +        read -u 3 line +        step=$line +        read -u 3 line +        plotFile=$line + +        while read -u 3 line; do +            echo "$line" >> $tempFile +        done + +        exec 3<&- +    else +        echo $usage +        exit 1 +    fi + +else # Operating in normal mode. +    while [ "$1" != "" ]; do + +        case $1 in + +        # Get num. +        "-n") +        shift + +        if [ ! -z $1 ]; then +            num=$1 +        else +            echo $usage +            exit 1 +        fi +        ;; + +        # Get min, max, and step. +        "-i") +        shift + +        if [ ! -z $1 ]; then +            min=$1 +        else +            echo $usage +            exit 1 +        fi + +        shift + +        if [ ! -z $1 ]; then +            max=$1 +        else +            echo $usage +            exit 1 +        fi + +        shift + +        if [ ! -z $1 ]; then +            step=$1 +        else +            echo $usage +            exit 1 +        fi +        ;; + +        # Get plotfile if requested. +        "-p") +        shift + +        if [ ! -z $1 ]; then +            plotFile=$1 +        else +            echo $usage +            exit 1 +        fi +        ;; + +        # Catch extraneous args. +        *) +        echo $usage +        exit 1 +        ;; + +        esac + +        shift +    done + +fi + +# Make sure required vars were set correctly. +if [ -z $num ] || [ -z $min ] || [ -z $max ] || [ -z $step ]; then +    echo $usage +    exit 1 +fi + +# I researched methods of checking whether a variable is an integer online +# for shell scripts (where variables aren't declared or strongly typed) and I +# found a method of comparing strings to regular expressions, where any +# character that isn't a numeric is incorrect. +case $num in +    ''|*[!0-9]*) echo $usage; exit 1;; +    *);; +esac + +case $min in +    ''|*[!0-9]*) echo $usage; exit 1;; +    *);; +esac + +case $max in +    ''|*[!0-9]*) echo $usage; exit 1;; +    *);; +esac + +case $step in +    ''|*[!0-9]*) echo $usage; exit 1;; +    *);; +esac + +# Run the first simulaton to ensure it gets done no matter the size of max. +beetlejuice + +# Run the remaining beetle simulations. +while [ $min -lt $max ] || [ $min -eq $max ]; do +    beetlejuice +done + +# Use awk to pull data from tempFile and print it out. +awk "$lifetimeAwk" "$tempFile" + +# If a plot was requested, plot the data into the given filename. +if [ ! -z $plotFile ]; then +    awk "$lifetimeAwk" "$tempFile" >> $dataFile + +    gnuplot <<- EOF +    set xlabel "Square Side (inches)" +    set ylabel "Mean Beetle Lifetime (seconds)" +    set term png +    set output "$plotFile" +    plot "$dataFile" using 1:2 title "" +EOF + +    rm -f $dataFile +fi + +rm -f $tempFile diff --git a/pgm7/TTT.c b/pgm7/TTT.c new file mode 100644 index 0000000..cec9023 --- /dev/null +++ b/pgm7/TTT.c @@ -0,0 +1,282 @@ +/************************************************************************ + 			     TTT.c + Castrated TTT server. No datagram queries, no timeouts. Assumes + completely well-behaved clients. Uses deprecated network address + translation. + + Phil Kearns + April 12, 1998 + Modified March, 2014 + + Adapted with GUI by Adam Carpenter - acarpent - acarpenter@email.wm.edu + Modified April 2017 +*************************************************************************/ + +#include "common.h" + +char board[9]; /* The tic-tac-toe board */ + +int main(int argc, char **argv) + +{ +  int listener;  /* fd for socket on which we get connection requests */ +  int Xfd, Ofd;  /* fds for sockets onto VCs to clients */ + +  struct sockaddr_in s1, Xaddr, Oaddr; +  int length,  sfile, i, moveto, result, Xresult, Oresult; +  unsigned short lport; +  int currentmove;  /* 0->X, 1->O */ +  char hostid[128], Xhandle[32], Ohandle[32]; +  struct tttmsg outmsg, inmsg; + +  if (argc != 1) { +    fprintf(stderr,"TTT:usage is TTT\n"); +    exit(1); +  } + +  if ( (sfile = open(SFILE, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { +    perror("TTT:sfile"); +    exit(1); +  } + +  if (gethostname(hostid,128) < 0){ +    perror("TTT:gethostname"); +        exit(1); +  } + +  i=0; +  while (hostid[i] != '\0') +    write(sfile,&hostid[i++], 1); +  write(sfile,"\0", 1); + +  if ( (listener = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) { +    perror("TTT:socket"); +    exit(1); +  } + +  bzero((char *) &s1, sizeof(s1)); +  s1.sin_family = AF_INET; +  s1.sin_addr.s_addr = INADDR_ANY;  /* Any of this host's interfaces is OK. */ +  s1.sin_port = 0;                  /* bind() will gimme unique port. */ +  if (bind(listener, (struct sockaddr *)&s1, sizeof(s1)) < 0) { +    perror("TTT:bind"); +    exit(1); +  } + +  length = sizeof(s1); +  if (getsockname(listener, (struct sockaddr *)&s1, (socklen_t *)&length) < 0) { +    perror("TTT:getsockname"); +    exit(1); +  } +  lport = s1.sin_port; +  write(sfile, &lport, sizeof(unsigned short)); +  close(sfile); + +  listen(listener,5); + +  length = sizeof(Xaddr); +  if ((Xfd = accept(listener, (struct sockaddr *)&Xaddr, (socklen_t *)&length)) < 0) { +    perror("TTT:accept X"); +    exit(1); +  } + +  /* Send WHO to X */ + +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type = WHO; +  putmsg(Xfd, &outmsg); + +  /* Await HANDLE from X */ + +  bzero((char *)&inmsg, sizeof(inmsg)); +  getmsg(Xfd, &inmsg); +  if (inmsg.type != HANDLE) protocol_error(HANDLE, &inmsg); +  strncpy(Xhandle, inmsg.data, 31); +  Xhandle[31] = '\0'; + +  length = sizeof(Oaddr); +  if ((Ofd = accept(listener, (struct sockaddr *)&Oaddr, (socklen_t *)&length)) < 0) { +    perror("TTT:accept O"); +    exit(1); +  } + +  /* Send WHO to O */ + +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type = WHO; +  putmsg(Ofd, &outmsg); + +  /* Await HANDLE from O */ + +  bzero((char *)&inmsg, sizeof(inmsg)); +  getmsg(Ofd, &inmsg); +  if (inmsg.type != HANDLE) protocol_error(HANDLE, &inmsg); +  strncpy(Ohandle, inmsg.data, 31); +  Ohandle[31] = '\0'; + +  /* WE HAVE A MATCH */ + +  for(i=0; i<9; i++) board[i]=' '; + +  /* Send MATCH to X */ + +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type=MATCH; +  strncpy(outmsg.data, Ohandle, 31); +  outmsg.data[31] = '\0'; +  outmsg.board[0] = 'X'; +  putmsg(Xfd, &outmsg); + +  /* Send MATCH to O */ + +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type=MATCH; +  strncpy(outmsg.data, Xhandle, 31); +  outmsg.data[31] = '\0'; +  outmsg.board[0] = 'O'; +  putmsg(Ofd, &outmsg); + +  while(1) { + +    /* Send  WHATMOVE to X */ + +    bzero((char *)&outmsg, sizeof(outmsg)); +    outmsg.type=WHATMOVE; +    for(i=0; i<9; i++) outmsg.board[i]=board[i]; +    putmsg(Xfd, &outmsg); + +    /* Await MOVE from X */ + +    bzero((char *)&inmsg, sizeof(inmsg)); +    getmsg(Xfd, &inmsg); + +    // End the match if player X resigned. +    if (inmsg.type == RESIGNED) { +        bzero((char *)&outmsg, sizeof(outmsg)); +        outmsg.type=ABORT; +        putmsg(Ofd, &outmsg); +        exit(0); +    } +    else if (inmsg.type != MOVE) { +        protocol_error(MOVE, &inmsg); +    } + +    currentmove = 0; /* X's move being reported*/ +    moveto = inmsg.res & 0xF; +    if (board[moveto] != ' ') { +      fprintf(stderr, "TTT: invalid move\n"); +      exit(1); +    } +    board[moveto] = 'X'; +    if ((result = check_board(currentmove)) != 0) break; + +    /* Send  WHATMOVE to O */ + +    bzero((char *)&outmsg, sizeof(outmsg)); +    outmsg.type=WHATMOVE; +    for(i=0; i<9; i++) outmsg.board[i]=board[i]; +    putmsg(Ofd, &outmsg); + +    /* Await MOVE from O */ + +    bzero((char *)&inmsg, sizeof(inmsg)); +    getmsg(Ofd, &inmsg); + +    // End the match if player O resigned. +    if (inmsg.type == RESIGNED) { +        bzero((char *)&outmsg, sizeof(outmsg)); +        outmsg.type=ABORT; +        putmsg(Xfd, &outmsg); +        exit(0); +    } +    else if (inmsg.type != MOVE) { +        protocol_error(MOVE, &inmsg); +    } + +    if (inmsg.type != MOVE) protocol_error(MOVE, &inmsg); +    currentmove = 1; /* O's move being reported*/ +    moveto = inmsg.res & 0xF; +    if (board[moveto] != ' ') { +      fprintf(stderr, "TTT: invalid move\n"); +      exit(1); +    } +    board[moveto] = 'O'; +    if ((result = check_board(currentmove)) != 0) break; +  } /* while */ + +  /* Match is over, report the result to both clients */ + +  if (result == 2) { +    Xresult = Oresult = 'D'; +  } +  else { /* result must be 1, we have a winner */ +    if (currentmove == 0) { Xresult = 'W'; Oresult = 'L';} +    else  { Xresult = 'L'; Oresult = 'W';} +  } + +  /* Send RESULT to X */ + +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type = RESULT; +  outmsg.res = Xresult; +  for(i=0; i<9; i++) outmsg.board[i]=board[i]; +  putmsg(Xfd, &outmsg); + +  /* Send RESULT to O */ + +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type = RESULT; +  outmsg.res = Oresult; +  for(i=0; i<9; i++) outmsg.board[i]=board[i]; +  putmsg(Ofd, &outmsg); + +  /* The game is finished */ + +  return(0); +} + + +/************************************************************************ +   check_board() returns 0 if no winner +                         1 if winner +                         2 if draw +   Brute force. +************************************************************************/ +int +check_board(player) +int player; /* X is 0, O is 1 */ +{ +  char match; +  int i,spaces; + +  match = (player?'O':'X'); + +  if (((board[0] == match) && (board[1] == match) && (board[2] == match)) || +      ((board[3] == match) && (board[4] == match) && (board[5] == match)) || +      ((board[6] == match) && (board[7] == match) && (board[8] == match)) || +      ((board[0] == match) && (board[3] == match) && (board[6] == match)) || +      ((board[1] == match) && (board[4] == match) && (board[7] == match)) || +      ((board[2] == match) && (board[5] == match) && (board[8] == match)) || +      ((board[0] == match) && (board[4] == match) && (board[8] == match)) || +      ((board[2] == match) && (board[4] == match) && (board[6] == match))) return 1; +  spaces = 0; +  for(i=0; i<9; i++) +    if (board[i] == ' ') { +      spaces++; +      break; +    } +  if (!spaces) return 2; +  return 0; +} + +void +dump_board(s,board) +FILE *s; +char board[]; +{ +  fprintf(s,"%c | %c | %c\n", board[0], board[1], board[2]); +  fprintf(s,"----------\n"); +  fprintf(s,"%c | %c | %c\n", board[3], board[4], board[5]); +  fprintf(s,"----------\n"); +  fprintf(s,"%c | %c | %c\n", board[6], board[7], board[8]); +} diff --git a/pgm7/child.c b/pgm7/child.c new file mode 100644 index 0000000..f9263e8 --- /dev/null +++ b/pgm7/child.c @@ -0,0 +1,75 @@ +/* + * child.c + * + * Created by Matt Welsh, 1995 + * + * Adapted by Adam Carpenter - acarpent - acarpenter@email.wm.edu + * April 2017 + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> +#include <string.h> +#include "child.h" + +/* Exec the named cmd as a child process, returning + * two pipes to communicate with the process, and + * the child's process ID */ +int start_child(char *cmd, FILE **readpipe, FILE **writepipe) { +    int childpid, pipe1[2], pipe2[2]; + +    if ((pipe(pipe1) < 0) || (pipe(pipe2) < 0) ) { +       perror("pipe"); exit(-1); +    } + +    if ((childpid = fork()) < 0) { +       perror("fork"); exit(-1); +    } else if (childpid > 0) {   /* Parent. */ +       close(pipe1[0]); close(pipe2[1]); +       /* Write to child on pipe1[1], read from child on pipe2[0]. */ +       *readpipe = fdopen(pipe2[0], "r"); +       *writepipe = fdopen(pipe1[1], "w"); +       setlinebuf(*writepipe); +       return childpid; + +    } else { /* Child. */ +       close(pipe1[1]); close(pipe2[0]); +       /* Read from parent on pipe1[0], write to parent on pipe2[1]. */ +       dup2(pipe1[0],0); dup2(pipe2[1],1); +       close(pipe1[0]); close(pipe2[1]); + +       if (execlp(cmd, cmd, NULL) < 0) +          perror("execlp"); +       /* Never returns */ +    } +    return 0;  // to keep the compiler happy +} + +void i_am_the_master_commander(char *handle, char *opphandle, FILE **writeTo) { +    /* +     * Look, don't touch. +     */ + +    if (strcmp(handle, "Adam") == 0) { +        fprintf(*writeTo, ".statusFrame.playerStatus configure -fg blue\n"); +    } +    else if (strcmp(handle, "Dan") == 0) { +        fprintf(*writeTo, ".statusFrame.playerStatus configure -fg red\n"); +    } +    else if (strcmp(handle, "Amy") == 0) { +        fprintf(*writeTo, ".statusFrame.playerStatus configure -fg \"light sea green\"\n"); +    } + +    if (strcmp(opphandle, "Adam") == 0) { +        fprintf(*writeTo, ".statusFrame.opponentStatus configure -fg blue\n"); +    } +    else if (strcmp(opphandle, "Dan") == 0) { +        fprintf(*writeTo, ".statusFrame.opponentStatus configure -fg red\n"); +    } +    else if (strcmp(opphandle, "Amy") == 0) { +        fprintf(*writeTo, ".statusFrame.opponentStatus configure -fg \"light sea green\"\n"); +    } + +} diff --git a/pgm7/child.h b/pgm7/child.h new file mode 100644 index 0000000..786bf7c --- /dev/null +++ b/pgm7/child.h @@ -0,0 +1,11 @@ +#ifndef _mdw_CHILD_H +#define _mdw_CHILD_H + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> + +int start_child(char *cmd, FILE **readpipe, FILE **writepipe);  +void i_am_the_master_commander(char *handle, char *opphandle, FILE **writeTo); + +#endif diff --git a/pgm7/common.h b/pgm7/common.h new file mode 100644 index 0000000..6775ade --- /dev/null +++ b/pgm7/common.h @@ -0,0 +1,68 @@ +#ifndef COMMON_H + +#define COMMON_H + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <unistd.h> + +/************************************************************ + All "messages" sent between server and clients in the + distributed tic-tac-toe system are in the format defined + by struct tttmsg. The structure of the message is determined + by the type field: + + type=A   WHO              no other fields used + type=B   HANDLE           data is the string handle for ttt +                            sending this message + type=C   MATCH            data is string handle of opponent; +                            board[0] is 'X' or 'O' to denote +                            character this ttt is using + type=D   WHATMOVE         board[] contains X/O/space chars +                            to denote current state + type=E   MOVE             res (ascii) indicates square into +                            which client is moving + type=F   RESULT           board[] contains X/O/space chars +                            to denote current state; +                            res =   W -> you win +                                    L -> you lose +                                    D -> draw +************************************************************/ + +#define WHO 'A' +#define HANDLE 'B' +#define MATCH 'C' +#define WHATMOVE 'D' +#define MOVE 'E' +#define RESULT 'F' +#define RESIGNED 'R' +#define ABORT '*' + +struct tttmsg{ +  char type;     /* Message type */ +  char board[9]; /* X/O */ +  char data[32]; /* null-terminated string */ +  char res;      /* integer data */ +}; + +#define SFILE "./serverloc" + +void putmsg(int, struct tttmsg *); +void getmsg(int, struct tttmsg *); +void protocol_error(char, struct tttmsg *); +void dumpmsg(struct tttmsg *); +void init_board(); +void dump_board(FILE *, char *); +int check_board(int); + +#endif diff --git a/pgm7/makefile b/pgm7/makefile new file mode 100644 index 0000000..3a3a73c --- /dev/null +++ b/pgm7/makefile @@ -0,0 +1,21 @@ +CC=gcc +CFLAGS=-g -Wall + +all:	TTT ttt + +TTT:	TTT.o msg.o +	gcc -o TTT TTT.o msg.o + +ttt:	ttt.o msg.o child.o +	gcc -o ttt ttt.o msg.o child.o + +msg.o:	msg.c common.h + +TTT.o:	TTT.c common.h + +ttt.o:	ttt.c common.h + +child.o: child.c child.h + +clean: +	rm -f *.o ttt TTT serverloc diff --git a/pgm7/msg.c b/pgm7/msg.c new file mode 100644 index 0000000..f6ae0a5 --- /dev/null +++ b/pgm7/msg.c @@ -0,0 +1,125 @@ +/************************************************************************ + + Functions for reading and writing "messages" over virtual circuits for + the tic-tac-toe system. + + Phil Kearns							          + April 12, 1998						          + 								          +*************************************************************************/ + +#include "common.h" + +void +putmsg(int s, struct tttmsg *m) +// int s               socket on which sending +// struct tttmsg *m    pointer to the message to be sent + +{ +  int bytes_sofar, to_go, num; +  char *addr; + +  bytes_sofar = 0; +  to_go = sizeof(struct tttmsg); + +  addr = (char *) m; + +  while (to_go > 0) { +    num = write(s, &(addr[bytes_sofar]), to_go); +    if (num < 0) { +      perror("putmsg"); +      exit(1); +    } +  to_go -= num; bytes_sofar += num;   +  } +} + +void +getmsg(int s, struct tttmsg *m) +// int s              socket on which receiving +// struct tttmsg *m   container for the message + +{ +  int bytes_sofar, to_go, num; +  char *addr; + +  bytes_sofar = 0; +  to_go = sizeof(struct tttmsg); + +  addr = (char *) m; + +  while (to_go > 0) { +    num = read(s, &(addr[bytes_sofar]), to_go); +    if (num < 0) { +      perror("putmsg"); +          exit(1); +    } +    if (num == 0) { +      fprintf(stderr, "Unexpected EOF\n"); +      exit(1); +    } +    to_go -= num; bytes_sofar += num; +  } +} + +void +protocol_error(char expected, struct tttmsg *offender) + +{ +  char *stype; + +  fprintf(stderr, "Protocol error: expected "); +  switch (expected) { +  case 'A': +    stype = "WHO"; break; +  case 'B': +    stype = "HANDLE"; break; +  case 'C': +    stype = "MATCH"; break; +  case 'D': +    stype = "WHATMOVE"; break; +  case 'E': +    stype = "MOVE"; break; +  case 'F': +    stype = "RESULT"; break; +  default: +    stype = "UNKNOWN"; break; +  } +  fprintf(stderr, "%s message; got following message:\n", stype); +  dumpmsg(offender); +  exit(1); +} + +void +dumpmsg(struct tttmsg *m) + +{ +  char *stype, datastring[32]; +  int i; +   +  switch (m->type) { +  case 'A': +    stype = "WHO"; break; +  case 'B': +    stype = "HANDLE"; break; +  case 'C': +    stype = "MATCH"; break; +  case 'D': +    stype = "WHATMOVE"; break; +  case 'E': +    stype = "MOVE"; break; +  case 'F': +    stype = "RESULT"; break; +  default: +    stype = "UNKNOWN"; break; +  } +  fprintf(stderr,"\tTYPE:     %s\n",stype); +  fprintf(stderr,"\tBOARD:    ["); +  for (i=0; i<8; i++) +    fprintf(stderr,"%c,",m->board[i]); +  fprintf(stderr,"%c]\n",m->board[8]); +  bcopy(datastring,&(m->data),31); +  datastring[31]='\0'; +  fprintf(stderr,"\tDATA:     %s\n", datastring); +  fprintf(stderr,"\tRES:      %c\n", m->res); +} diff --git a/pgm7/ttt.c b/pgm7/ttt.c new file mode 100644 index 0000000..da728b3 --- /dev/null +++ b/pgm7/ttt.c @@ -0,0 +1,386 @@ +/************************************************************************ + 			         ttt.c + Simple ttt client. No queries, no timeouts. + Uses deprecated address translation functions. + + Phil Kearns + April 12, 1998 + Modified March 2014 + + Adapted with GUI by Adam Carpenter - acarpent - acarpenter@email.wm.edu + Modified April 2017 +************************************************************************/ + + +#include "common.h" +#include "child.h" + + +void dump_board(FILE *s, char *board) { +  fprintf(s,"%c | %c | %c\n", board[0], board[1], board[2]); +  fprintf(s,"----------\n"); +  fprintf(s,"%c | %c | %c\n", board[3], board[4], board[5]); +  fprintf(s,"----------\n"); +  fprintf(s,"%c | %c | %c\n", board[6], board[7], board[8]); +} + + +void drawWin(FILE **writeTo, char *board) { + +    // Check rows for win. +    if      (board[0] + board[1] + board[2] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 50 300 50 -width 5 -fill red\n"); +    else if (board[0] + board[1] + board[2] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 50 300 50 -width 5 -fill red\n"); +    if      (board[3] + board[4] + board[5] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 150 300 150 -width 5 -fill red\n"); +    else if (board[3] + board[4] + board[5] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 150 300 150 -width 5 -fill red\n"); +    if      (board[6] + board[7] + board[8] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 250 300 250 -width 5 -fill red\n"); +    else if (board[6] + board[7] + board[8] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 250 300 250 -width 5 -fill red\n"); + +    // Check columns for win. +    if      (board[0] + board[3] + board[6] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 50 0 50 300 -width 5 -fill red\n"); +    else if (board[0] + board[3] + board[6] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 50 0 50 300 -width 5 -fill red\n"); +    if      (board[1] + board[4] + board[7] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 150 0 150 300 -width 5 -fill red\n"); +    else if (board[1] + board[4] + board[7] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 150 0 150 300 -width 5 -fill red\n"); +    if      (board[2] + board[5] + board[8] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 250 0 250 300 -width 5 -fill red\n"); +    else if (board[2] + board[5] + board[8] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 250 0 250 300 -width 5 -fill red\n"); + +    // Check diagonals for win. +    if      (board[0] + board[4] + board[8] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 0 300 300 -width 5 -fill red\n"); +    else if (board[0] + board[4] + board[8] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 0 300 300 -width 5 -fill red\n"); +    if      (board[2] + board[4] + board[6] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 300 300 0 -width 5 -fill red\n"); +    else if (board[2] + board[4] + board[6] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 300 300 0 -width 5 -fill red\n"); + +} + + +void drawBoard(FILE **writeTo, int space, char symbol) { +    switch (space + 1) { +      case 1: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 0 0 100 100 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 0 100 100 0 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 0 0 100 100 -width 5\n"); +          } +          break; + +      case 2: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 100 0 200 100 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 100 100 200 0 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 100 0 200 100 -width 5\n"); +          } +          break; + +      case 3: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 200 0 300 100 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 200 100 300 0 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 200 0 300 100 -width 5\n"); +          } +          break; + +      case 4: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 0 100 100 200 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 0 200 100 100 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 0 100 100 200 -width 5\n"); +          } +          break; + +      case 5: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 100 100 200 200 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 100 200 200 100 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 100 100 200 200 -width 5\n"); +          } +          break; + +      case 6: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 200 100 300 200 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 200 200 300 100 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 200 100 300 200 -width 5\n"); +          } +          break; + +      case 7: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 0 200 100 300 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 0 300 100 200 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 0 200 100 300 -width 5\n"); +          } +          break; + +      case 8: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 100 200 200 300 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 100 300 200 200 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 100 200 200 300 -width 5\n"); +          } +          break; + +      case 9: +          if (symbol == 'X') { +              fprintf(*writeTo, ".gameCanvas create line 200 200 300 300 -width 5\n"); +              fprintf(*writeTo, ".gameCanvas create line 200 300 300 200 -width 5\n"); +          } +          else { +              fprintf(*writeTo, ".gameCanvas create oval 200 200 300 300 -width 5\n"); +          } +          break; +    } +} + + +int main(int argc, char **argv) { +  char hostid[128], handle[32], opphandle[32]; +  char my_symbol; /* X or O ... specified by server in MATCH message */ +  char board[9]; +  char prevBoard[9]; +  unsigned short xrport; +  int sock, sfile; +  struct sockaddr_in remote; +  struct hostent *h; +  int num, i, move, valid, finished; +  struct tttmsg inmsg, outmsg; + +  if (argc != 1) { +    fprintf(stderr,"ttt:usage is ttt\n"); +    exit(1); +  } + +  /* Get host,port of server from file. */ +  if ( (sfile = open(SFILE, O_RDONLY)) < 0) { +    perror("TTT:sfile"); +    exit(1); +  } +  i=0; +  while (1) { +    num = read(sfile, &hostid[i], 1); +    if (num == 1) { +      if (hostid[i] == '\0') break; +      else i++; +    } +    else { +      fprintf(stderr, "ttt:error reading hostname\n"); +      exit(1); +    } +  } +  if (read(sfile, &xrport, sizeof(int)) != sizeof(unsigned short)) { +    fprintf(stderr, "ttt:error reading port\n"); +      exit(1); +  } +  close(sfile); + + +  /* Got the info. Connect. */ +  if ( (sock = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) { +    perror("ttt:socket"); +    exit(1); +  } + +  bzero((char *) &remote, sizeof(remote)); +  remote.sin_family = AF_INET; +  if ((h = gethostbyname(hostid)) == NULL) { +    perror("ttt:gethostbyname"); +    exit(1); +  } +  bcopy((char *)h->h_addr, (char *)&remote.sin_addr, h->h_length); +  remote.sin_port = xrport; +  if ( connect(sock, (struct sockaddr *)&remote, sizeof(remote)) < 0) { +    perror("ttt:connect"); +    exit(1); +  } + +  /* We're connected to the server. Engage in the prescribed dialog */ + +  /* Await WHO */ +  bzero((char *)&inmsg, sizeof(inmsg)); +  getmsg(sock, &inmsg); +  if (inmsg.type != WHO) protocol_error(WHO, &inmsg); + +  /* Send HANDLE */ +  printf("Enter handle (31 char max):"); +  fgets(handle, 31, stdin); +  bzero((char *)&outmsg, sizeof(outmsg)); +  outmsg.type = HANDLE; +  strncpy(outmsg.data, handle, 31); outmsg.data[31] = '\0'; +  putmsg(sock, &outmsg); + +  // Set up ttt.tcl GUI. +  FILE *readFrom; +  FILE *writeTo; +  char result[80]; +  int childpid; + +  // Fork off child process and use it to run wish with the ttt.tcl script. +  if ((childpid = start_child("wish", &readFrom, &writeTo)) <= 0) { +      fprintf(stderr, "ttt: couldn't start child GUI.\n"); +      exit(1); +  } + +  fprintf(writeTo, "source ttt.tcl\n"); +  sscanf(handle, "%s[^\n]", handle); +  fprintf(writeTo, ".statusFrame.playerStatus configure -text %s\n", handle); + +  /* Await MATCH */ +  bzero((char *)&inmsg, sizeof(inmsg)); +  getmsg(sock, &inmsg); +  if (inmsg.type != MATCH) protocol_error(MATCH, &inmsg); +  my_symbol = inmsg.board[0]; +  strncpy(opphandle, inmsg.data, 31); opphandle[31] = '\0'; +  sscanf(opphandle, "%s[^\n]", opphandle); + +  i_am_the_master_commander(handle, opphandle, &writeTo); + +  if (my_symbol == 'X') { +      strcat(handle, "(X)"); +      strcat(opphandle, "(O)"); +      fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Your move.\"\n"); +  } +  else { +      strcat(handle, "(O)"); +      strcat(opphandle, "(X)"); +      fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Awaiting opponent move...\"\n"); +  } + +  fprintf(writeTo, ".statusFrame.playerStatus configure -text %s\n", handle); +  fprintf(writeTo, ".statusFrame.opponentStatus configure -text %s\n", opphandle); + +  /* In the match */ +  for(i = 0; i < 9; i++) { +      board[i]=' '; +      prevBoard[i] = ' '; +  } + +  finished = 0; + +  while(!finished){ +    /* Await WHATMOVE/RESULT from server */ +    bzero((char *)&inmsg, sizeof(inmsg)); +    getmsg(sock, &inmsg); +    switch (inmsg.type) { + +    case WHATMOVE: +      fprintf(writeTo, "beep\n"); +      fprintf(writeTo, ".buttonFrame.resignButton configure -state normal\n"); + +      for (i = 0; i < 9; i++) { +          board[i] = inmsg.board[i]; +      } + +      // Draw the new board. +      for (i = 0; i < 9; i++) { +          if (board[i] != prevBoard[i]) { +              // Remove taken spaces from game board canvas and draw board. +              drawBoard(&writeTo, i, board[i]); +          } +      } +      strcpy(prevBoard, board); + +      do { +        valid = 0; +        fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Your move.\"\n"); +        fprintf(writeTo, "global myTurn; set myTurn 1\n"); + +        if (fgets(result, 80, readFrom) <= 0) { +            fprintf(stderr, "ttt: GUI terminated.\n"); +            exit(0); +        } + +        move = atoi(result); + +        // If player resigned, alert the server and end the client. +        if (result[0] == 'R') { +            bzero((char *)&outmsg, sizeof(outmsg)); +            outmsg.type = RESIGNED; +            putmsg(sock, &outmsg); +            fprintf(writeTo, "global myTurn; set myTurn 0\n"); +            fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You lose. (You resigned)\"\n"); +            fprintf(writeTo, ".buttonFrame.exitButton configure -state normal\n"); +            fprintf(writeTo, ".buttonFrame.resignButton configure -state disabled\n"); +            exit(0); +        } + +        if ((move >= 1) && (move <= 9)) valid = 1; +        if ((valid) && (board[move-1] != ' '))valid=0; +        if (valid == 0) fprintf(writeTo, "beep\n"); + +      } while (!valid); + +      /* Send MOVE to server */ +      bzero((char *)&outmsg, sizeof(outmsg)); +      outmsg.type = MOVE; +      sprintf(&outmsg.res, "%c", move-1); +      putmsg(sock, &outmsg); +      fprintf(writeTo, "global myTurn; set myTurn 0\n"); +      fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Awaiting opponent move...\"\n"); +      fprintf(writeTo, ".buttonFrame.resignButton configure -state disabled\n"); +      drawBoard(&writeTo, move - 1, my_symbol); +      break; + +    case RESULT: +        for(i=0; i<9; i++) board[i]=inmsg.board[i]; + +        // Draw the new board. +        for (i = 0; i < 9; i++) { +            if (board[i] != prevBoard[i]) { +                drawBoard(&writeTo, i, board[i]); +            } +        } + +        switch (inmsg.res) { +            case 'W': +                // printf("You win\n"); +                fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You Win!\"\n"); +                break; + +            case 'L': +                // printf("You lose\n"); +                fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You Lose!\"\n"); +                break; + +            case 'D': +                // printf("Draw\n"); +                fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Draw\"\n"); +                break; + +            default: +                fprintf(stderr,"Invalid result code\n"); +                exit(1); +        } + +        finished = 1; +        fprintf(writeTo, ".buttonFrame.exitButton configure -state normal\n"); +        drawWin(&writeTo, board); +        break; + +    case ABORT: +        fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You won! (Opponent resigned)\"\n"); +        fprintf(writeTo, ".buttonFrame.exitButton configure -state normal\n"); +        fprintf(writeTo, ".buttonFrame.resignButton configure -state disabled\n"); +        fprintf(stderr, "Opponent resigned match.\n"); +        exit(0); + +    default: +        protocol_error(MOVE, &inmsg); +    } + +  } +  return(0); +} diff --git a/pgm7/ttt.tcl b/pgm7/ttt.tcl new file mode 100644 index 0000000..5c6fc0c --- /dev/null +++ b/pgm7/ttt.tcl @@ -0,0 +1,123 @@ +#!/usr/bin/tclsh +# +# ttt.tcl +# +# Adam Carpenter - 2017 - acarpent - acarpenter@email.wm.edu +# Utilizes code from P. Kearns + +# **** Initialize Variables **** +global soundFlag; set soundFlag 1 +global playerSide; set playerSide - +global playerHandle; set playerHandle (Player1) +global opponentHandle; set opponentHandle (Player2) +global myTurn; set myTurn 0 + + +# **** Initialize Procedures**** +proc resignMatch {} { +    global myTurn +    global playerSide + +    if {$myTurn == 1} { +        set myTurn 0 +        puts stdout R +        flush stdout +    } +} + +proc soundToggle {} { +    global soundFlag +    set soundFlag [expr $soundFlag * -1] + +    if {$soundFlag > 0} { +        .buttonFrame.soundButton configure -text "Silent" +    } else { +        .buttonFrame.soundButton configure -text "Sound" +    } + +} + +proc exitMatch {} { +    exit 0 +} + +proc playerMove {tag} { +    global myTurn +    global playerSide +    global array taken + +    if {$myTurn == 1} { +        set myTurn 0 +        puts stdout $tag +        flush stdout +    } + +} + +proc beep {} { +    global soundFlag + +    if {$soundFlag > 0} { +        bell +        bell -nice +    } + +} + +# **** Main **** + +# Create canvas. +canvas .gameCanvas -width 300 -height 300 -bg white + +# Create board spaces. +.gameCanvas create rectangle 0    0    100  100 -fill white -outline white -tag 1 +.gameCanvas create rectangle 100  0    200  100 -fill white -outline white -tag 2 +.gameCanvas create rectangle 200  0    300  100 -fill white -outline white -tag 3 +.gameCanvas create rectangle 0    100  100  200 -fill white -outline white -tag 4 +.gameCanvas create rectangle 100  100  200  200 -fill white -outline white -tag 5 +.gameCanvas create rectangle 200  100  300  200 -fill white -outline white -tag 6 +.gameCanvas create rectangle 0    200  100  300 -fill white -outline white -tag 7 +.gameCanvas create rectangle 100  200  200  300 -fill white -outline white -tag 8 +.gameCanvas create rectangle 200  200  300  300 -fill white -outline white -tag 9 + +# Bind board spaces with rectangles. +.gameCanvas bind 1 <Button-1> {playerMove 1} +.gameCanvas bind 2 <Button-1> {playerMove 2} +.gameCanvas bind 3 <Button-1> {playerMove 3} +.gameCanvas bind 4 <Button-1> {playerMove 4} +.gameCanvas bind 5 <Button-1> {playerMove 5} +.gameCanvas bind 6 <Button-1> {playerMove 6} +.gameCanvas bind 7 <Button-1> {playerMove 7} +.gameCanvas bind 8 <Button-1> {playerMove 8} +.gameCanvas bind 9 <Button-1> {playerMove 9} + +# Draw board grid. +.gameCanvas create line 100 0 100 300 -width 4 +.gameCanvas create line 200 0 200 300 -width 4 +.gameCanvas create line 0 100 300 100 -width 4 +.gameCanvas create line 0 200 300 200 -width 4 + +pack .gameCanvas + +# Set up game status frame and labels. +frame .statusFrame -width 300 -height 100 -bg grey +pack .statusFrame -expand 1 -fill x +label .statusFrame.playerStatus  -text "?" -bg grey -fg white +label .statusFrame.vs -text "VS" -bg grey -fg white +label .statusFrame.opponentStatus  -text "?" -bg grey -fg white +label .statusFrame.gameStatus  -text "\nAwaiting match..." -bg grey -fg white +pack .statusFrame.gameStatus -expand 1 -fill x -side bottom +pack .statusFrame.playerStatus -expand 1 -fill x -side left +pack .statusFrame.vs -expand 1 -fill x -side left +pack .statusFrame.opponentStatus -expand 1 -fill x -side left + + +# Set up button frame and buttons. +frame .buttonFrame +pack .buttonFrame -expand 1 -fill x +button .buttonFrame.soundButton -text "Silent" -command soundToggle +button .buttonFrame.resignButton -text "Resign" -command resignMatch -state disabled +button .buttonFrame.exitButton -text "Exit" -command exitMatch -state disabled +pack .buttonFrame.soundButton .buttonFrame.resignButton .buttonFrame.exitButton -expand 1 -fill x -side left + +bell |