From 379c2c17e68d5d471a6a9673b7e9cd1fb9d99bbb Mon Sep 17 00:00:00 2001 From: 53hornet <53hornet@gmail.com> Date: Sat, 2 Feb 2019 22:59:54 -0500 Subject: Init. --- pgm1/beetle.c | 140 +++++++ pgm1/makefile | 36 ++ pgm2/input.txt | 5 + pgm2/makefile | 31 ++ pgm2/p2test/d1/a.out | Bin 0 -> 10766 bytes pgm2/p2test/d1/f1 | 11 + pgm2/p2test/f1 | 1 + pgm2/p2test/f2 | 3 + pgm2/p2test/f3 | 2 + pgm2/p2test/grepout | 6 + pgm2/rgpp.c | 381 ++++++++++++++++++ pgm3/accessed.c | 122 ++++++ pgm3/pgm3test.d/fileinfo.c | 30 ++ pgm3/pgm3test.d/link.c | 30 ++ pgm3/pgm3test.d/script | 24 ++ pgm3/pgm3test.d/subdir/subfileinfo.c | 30 ++ pgm3/pgm3test.d/subdir/subsubdir/file1 | Bin 0 -> 1023 bytes pgm3/pgm3test.d/subdir/subsubdir/file2 | Bin 0 -> 1023 bytes pgm3/pgm3test.d/subdir/subsubdir/file3 | Bin 0 -> 1023 bytes pgm3/pgm3test.d/symlink.c | 30 ++ pgm3/report.c | 232 +++++++++++ pgm3/totalsize.c | 126 ++++++ pgm4/TTT.c | 590 ++++++++++++++++++++++++++++ pgm4/inet_addrs.d/Makefile | 10 + pgm4/inet_addrs.d/README | 11 + pgm4/inet_addrs.d/getaddrs.c | 171 ++++++++ pgm4/inet_addrs.d/gethost.c | 57 +++ pgm4/inet_dgrams.d/Makefile | 17 + pgm4/inet_dgrams.d/README | 11 + pgm4/inet_dgrams.d/fancy_recv_udp.c | 117 ++++++ pgm4/inet_dgrams.d/recv_udp.c | 115 ++++++ pgm4/inet_dgrams.d/send_udp.c | 87 +++++ pgm4/inet_streams.d/Makefile | 13 + pgm4/inet_streams.d/README | 3 + pgm4/inet_streams.d/inet_rstream.c | 110 ++++++ pgm4/inet_streams.d/inet_wstream.c | 91 +++++ pgm4/makefile | 19 + pgm4/ttt.c | 257 ++++++++++++ pgm4/ttt.h | 33 ++ pgm4/tttmsg.c | 55 +++ pgm5/counter.c | 690 +++++++++++++++++++++++++++++++++ pgm5/makefile | 11 + pgm5/p5test/file0 | 17 + pgm5/p5test/file1 | 2 + pgm5/p5test/file2 | 3 + pgm5/p5test/file3 | 9 + pgm5/p5test/file4 | 5 + pgm5/p5test/file5 | 11 + pgm5/p5test/file6 | 1 + pgm5/prodcons.d/Makefile | 10 + pgm5/prodcons.d/README | 2 + pgm5/prodcons.d/prodcons.c | 124 ++++++ pgm5/run.sh | 13 + pgm6/beetle | Bin 0 -> 11115 bytes pgm6/boiler.sh | 242 ++++++++++++ pgm7/TTT.c | 282 ++++++++++++++ pgm7/child.c | 75 ++++ pgm7/child.h | 11 + pgm7/common.h | 68 ++++ pgm7/makefile | 21 + pgm7/msg.c | 125 ++++++ pgm7/ttt.c | 386 ++++++++++++++++++ pgm7/ttt.tcl | 123 ++++++ 63 files changed, 5238 insertions(+) create mode 100644 pgm1/beetle.c create mode 100755 pgm1/makefile create mode 100644 pgm2/input.txt create mode 100755 pgm2/makefile create mode 100644 pgm2/p2test/d1/a.out create mode 100644 pgm2/p2test/d1/f1 create mode 100644 pgm2/p2test/f1 create mode 100644 pgm2/p2test/f2 create mode 100644 pgm2/p2test/f3 create mode 100644 pgm2/p2test/grepout create mode 100644 pgm2/rgpp.c create mode 100644 pgm3/accessed.c create mode 100644 pgm3/pgm3test.d/fileinfo.c create mode 100644 pgm3/pgm3test.d/link.c create mode 100644 pgm3/pgm3test.d/script create mode 100644 pgm3/pgm3test.d/subdir/subfileinfo.c create mode 100644 pgm3/pgm3test.d/subdir/subsubdir/file1 create mode 100644 pgm3/pgm3test.d/subdir/subsubdir/file2 create mode 100644 pgm3/pgm3test.d/subdir/subsubdir/file3 create mode 100644 pgm3/pgm3test.d/symlink.c create mode 100644 pgm3/report.c create mode 100644 pgm3/totalsize.c create mode 100644 pgm4/TTT.c create mode 100644 pgm4/inet_addrs.d/Makefile create mode 100644 pgm4/inet_addrs.d/README create mode 100644 pgm4/inet_addrs.d/getaddrs.c create mode 100644 pgm4/inet_addrs.d/gethost.c create mode 100644 pgm4/inet_dgrams.d/Makefile create mode 100644 pgm4/inet_dgrams.d/README create mode 100644 pgm4/inet_dgrams.d/fancy_recv_udp.c create mode 100644 pgm4/inet_dgrams.d/recv_udp.c create mode 100644 pgm4/inet_dgrams.d/send_udp.c create mode 100644 pgm4/inet_streams.d/Makefile create mode 100644 pgm4/inet_streams.d/README create mode 100644 pgm4/inet_streams.d/inet_rstream.c create mode 100644 pgm4/inet_streams.d/inet_wstream.c create mode 100644 pgm4/makefile create mode 100644 pgm4/ttt.c create mode 100644 pgm4/ttt.h create mode 100644 pgm4/tttmsg.c create mode 100644 pgm5/counter.c create mode 100644 pgm5/makefile create mode 100644 pgm5/p5test/file0 create mode 100644 pgm5/p5test/file1 create mode 100644 pgm5/p5test/file2 create mode 100644 pgm5/p5test/file3 create mode 100644 pgm5/p5test/file4 create mode 100644 pgm5/p5test/file5 create mode 100644 pgm5/p5test/file6 create mode 100644 pgm5/prodcons.d/Makefile create mode 100644 pgm5/prodcons.d/README create mode 100644 pgm5/prodcons.d/prodcons.c create mode 100644 pgm5/run.sh create mode 100644 pgm6/beetle create mode 100644 pgm6/boiler.sh create mode 100644 pgm7/TTT.c create mode 100644 pgm7/child.c create mode 100644 pgm7/child.h create mode 100644 pgm7/common.h create mode 100644 pgm7/makefile create mode 100644 pgm7/msg.c create mode 100644 pgm7/ttt.c create mode 100644 pgm7/ttt.tcl 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 +#include +#include +#include + +/* + * 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 Binary files /dev/null and b/pgm2/p2test/d1/a.out 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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +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 Binary files /dev/null and b/pgm3/pgm3test.d/subdir/subsubdir/file1 differ diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file2 b/pgm3/pgm3test.d/subdir/subsubdir/file2 new file mode 100644 index 0000000..1bce2be Binary files /dev/null and b/pgm3/pgm3test.d/subdir/subsubdir/file2 differ diff --git a/pgm3/pgm3test.d/subdir/subsubdir/file3 b/pgm3/pgm3test.d/subdir/subsubdir/file3 new file mode 100644 index 0000000..1bce2be Binary files /dev/null and b/pgm3/pgm3test.d/subdir/subsubdir/file3 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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include + + +// **** 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 +#include + +#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 Binary files /dev/null and b/pgm6/beetle 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 +#include +#include +#include +#include +#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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************************************ + 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 {playerMove 1} +.gameCanvas bind 2 {playerMove 2} +.gameCanvas bind 3 {playerMove 3} +.gameCanvas bind 4 {playerMove 4} +.gameCanvas bind 5 {playerMove 5} +.gameCanvas bind 6 {playerMove 6} +.gameCanvas bind 7 {playerMove 7} +.gameCanvas bind 8 {playerMove 8} +.gameCanvas bind 9 {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 -- cgit v1.2.3