summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pgm1/beetle.c140
-rwxr-xr-xpgm1/makefile36
-rw-r--r--pgm2/input.txt5
-rwxr-xr-xpgm2/makefile31
-rw-r--r--pgm2/p2test/d1/a.outbin0 -> 10766 bytes
-rw-r--r--pgm2/p2test/d1/f111
-rw-r--r--pgm2/p2test/f11
-rw-r--r--pgm2/p2test/f23
-rw-r--r--pgm2/p2test/f32
-rw-r--r--pgm2/p2test/grepout6
-rw-r--r--pgm2/rgpp.c381
-rw-r--r--pgm3/accessed.c122
-rw-r--r--pgm3/pgm3test.d/fileinfo.c30
-rw-r--r--pgm3/pgm3test.d/link.c30
-rw-r--r--pgm3/pgm3test.d/script24
-rw-r--r--pgm3/pgm3test.d/subdir/subfileinfo.c30
-rw-r--r--pgm3/pgm3test.d/subdir/subsubdir/file1bin0 -> 1023 bytes
-rw-r--r--pgm3/pgm3test.d/subdir/subsubdir/file2bin0 -> 1023 bytes
-rw-r--r--pgm3/pgm3test.d/subdir/subsubdir/file3bin0 -> 1023 bytes
-rw-r--r--pgm3/pgm3test.d/symlink.c30
-rw-r--r--pgm3/report.c232
-rw-r--r--pgm3/totalsize.c126
-rw-r--r--pgm4/TTT.c590
-rw-r--r--pgm4/inet_addrs.d/Makefile10
-rw-r--r--pgm4/inet_addrs.d/README11
-rw-r--r--pgm4/inet_addrs.d/getaddrs.c171
-rw-r--r--pgm4/inet_addrs.d/gethost.c57
-rw-r--r--pgm4/inet_dgrams.d/Makefile17
-rw-r--r--pgm4/inet_dgrams.d/README11
-rw-r--r--pgm4/inet_dgrams.d/fancy_recv_udp.c117
-rw-r--r--pgm4/inet_dgrams.d/recv_udp.c115
-rw-r--r--pgm4/inet_dgrams.d/send_udp.c87
-rw-r--r--pgm4/inet_streams.d/Makefile13
-rw-r--r--pgm4/inet_streams.d/README3
-rw-r--r--pgm4/inet_streams.d/inet_rstream.c110
-rw-r--r--pgm4/inet_streams.d/inet_wstream.c91
-rw-r--r--pgm4/makefile19
-rw-r--r--pgm4/ttt.c257
-rw-r--r--pgm4/ttt.h33
-rw-r--r--pgm4/tttmsg.c55
-rw-r--r--pgm5/counter.c690
-rw-r--r--pgm5/makefile11
-rw-r--r--pgm5/p5test/file017
-rw-r--r--pgm5/p5test/file12
-rw-r--r--pgm5/p5test/file23
-rw-r--r--pgm5/p5test/file39
-rw-r--r--pgm5/p5test/file45
-rw-r--r--pgm5/p5test/file511
-rw-r--r--pgm5/p5test/file61
-rw-r--r--pgm5/prodcons.d/Makefile10
-rw-r--r--pgm5/prodcons.d/README2
-rw-r--r--pgm5/prodcons.d/prodcons.c124
-rw-r--r--pgm5/run.sh13
-rw-r--r--pgm6/beetlebin0 -> 11115 bytes
-rw-r--r--pgm6/boiler.sh242
-rw-r--r--pgm7/TTT.c282
-rw-r--r--pgm7/child.c75
-rw-r--r--pgm7/child.h11
-rw-r--r--pgm7/common.h68
-rw-r--r--pgm7/makefile21
-rw-r--r--pgm7/msg.c125
-rw-r--r--pgm7/ttt.c386
-rw-r--r--pgm7/ttt.tcl123
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.out
new file mode 100644
index 0000000..c1ea1d0
--- /dev/null
+++ b/pgm2/p2test/d1/a.out
Binary files differ
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/file1
new file mode 100644
index 0000000..1bce2be
--- /dev/null
+++ b/pgm3/pgm3test.d/subdir/subsubdir/file1
Binary files differ
diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file2 b/pgm3/pgm3test.d/subdir/subsubdir/file2
new file mode 100644
index 0000000..1bce2be
--- /dev/null
+++ b/pgm3/pgm3test.d/subdir/subsubdir/file2
Binary files differ
diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file3 b/pgm3/pgm3test.d/subdir/subsubdir/file3
new file mode 100644
index 0000000..1bce2be
--- /dev/null
+++ b/pgm3/pgm3test.d/subdir/subsubdir/file3
Binary files differ
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/beetle
new file mode 100644
index 0000000..91a4009
--- /dev/null
+++ b/pgm6/beetle
Binary files differ
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