commit 1e7bf3f5e02ffa27c68f4da246c3a4d6c1968c39 Author: Dane MacFadden Date: Fri Jul 25 19:06:44 2025 -0600 merge fix diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0d8259b --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +CC = gcc +CFLAGS = -g +CPPFLAGS = -Wall -Wextra -pedantic -std=gnu90 + +chatty: main.o liblist.a + $(CC) $(CFLAGS) -L. -o chatty main.o -llist -lpthread + +main.o: main.c + $(CC) $(CFLAGS) $(CPPFLAGS) -I. -c -o main.o main.c + +liblist.a: list_adders.o list_removers.o list_movers.o + ar -rcs liblist.a list_adders.o list_removers.o list_movers.o + +list_adders.o: list_adders.c + $(CC) $(CFLAGS) $(CPPFLAGS) -I. -c -o list_adders.o list_adders.c + +list_movers.o: list_movers.c + $(CC) $(CFLAGS) $(CPPFLAGS) -I. -c -o list_movers.o list_movers.c + +list_removers.o: list_removers.c + $(CC) $(CFLAGS) $(CPPFLAGS) -I. -c -o list_removers.o list_removers.c + + +clean: + rm -f *.o chatty liblist.a diff --git a/README.md b/README.md new file mode 100644 index 0000000..4bdc543 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +Chatty is a simple, multithreaded chat program by Dane MacFadden. +It's inspired by an assignment I did in university that was a similar project +and is intended to be an exercise in networked and multithreaded programming. +Check out Beej's guide to network programming (and his other guides) if you want +to build something similar. Much of my code is based on his client and server. +Shoutout James for helping me write the list library back in the day. diff --git a/chatty b/chatty new file mode 100755 index 0000000..cc8a6d0 Binary files /dev/null and b/chatty differ diff --git a/liblist.a b/liblist.a new file mode 100644 index 0000000..9226029 Binary files /dev/null and b/liblist.a differ diff --git a/list.h b/list.h new file mode 100644 index 0000000..a5a8db8 --- /dev/null +++ b/list.h @@ -0,0 +1,151 @@ +/* + Dane MacFadden + James Hom +*/ + +#ifndef __LIST_H__ +#define __LIST_H__ + +typedef struct node { + void *data; + struct node *next; + struct node *previous; +} NODE; + +typedef struct list { + + int size; + NODE *current; + NODE *head; + NODE *tail; + +} LIST; + +/* + * Create an empty list + * Return: Pointer to new empty list + */ +LIST *ListCreate(void); + +/* + * Get the number of items in the list + * Return: Integer containing number of items + */ +int ListCount(LIST *list); + +/* + * Get the first item in the list and make it the current item + * list: Pointer to a list + * Return: Pointer to head's data + */ +void *ListFirst(LIST *list); + +/* + * Get the last item in the list and make it the current item + * list: Pointer to a list + * Return: Pointer to tail's data + */ +void *ListLast(LIST *list); + +/* Get the item in the list after the cursor and make the cursor point to it + * list: Pointer to a list + * Return: Pointer to next item's data + */ +void *ListNext(LIST *list); + +/* + * Get the item before the cursor and set the cursor to it. + * list: Pointer to a list + * Return: Pointer to previous item's data + */ +void *ListPrev(LIST *list); + +/* Get current item in list + * list: Pointer to list + * Return: Pointer to current item's data +*/ +void *ListCurr(LIST *list); + +/* + Add item to list after current item, + switch current item to the item we just added. + If current item is at the end, item is added at the end. + list: List pointer + item: Pointer to an item to add to the list + Returns: 0 if successful, -1 otherwise +*/ +int ListAdd(LIST *list, void *item); + +/* + * Insert an item right before the current item, switch cursor to new item. + * If current is the start of the list, add to the start. + * list: Pointer to list + * item: Pointer to item to add + * Return: 0 if successful, -1 otherwise + */ +int ListInsert(LIST *list, void *item); + +/* + * Add an item to the end of the list. Keep cursor where it is. + * list: Pointer to list + * item: Pointer to item to add + * Return: 0 if successful, -1 otherwise + */ +int ListAppend(LIST *list, void *item); + +/* + * Add item to the front of the list + * list: Pointer to list + * item: Pointer to item to add + * Return: 0 if successful, -1 otherwise + */ +int ListPrepend(LIST *list, void *item); + +/* + * Delete current item from list, free current item, and make next item current + * list: Pointer to list + * Return: 0 on success, -1 on failure + */ +int ListRemove(LIST *list); + +/* + * Adds list2 to the end of list1 + * Current pointer set to current pointer of list 1. List2 ceases to exist + * list1: Pointer to list1 + * list2: Pointer to list2 + */ +void ListConcat(LIST *list1, LIST *list2); + +/* + * Deletes list + * Ensure you set your pointer to NULL after calling so you don't get + * dangling pointers! + * list: Pointer to list + */ +void ListFree(LIST *list); + +/* + * Returns last item and takes it out of the list + * list: Pointer to list + * Returns Pointer to last item that was just deleted from list + */ +void *ListTrim(LIST *list); + +/* + * Search list starting at current item. + * Finishes at end of list or when a match is found + * list: Pointer to list + * comparator: Routine that returns 0 + * if the item and comparisonArg don't match, + * or 1 if they do. If a match is found the current pointer + * is left at the matched item + * If no match is found, + * current pointer is left at the end + * Return: Pointer to matching item if found, otherwise NULL pointer + */ +void *ListSearch(LIST *list, int (*comparator)(void *, void *), + void *comparisonArg); + +#endif + + diff --git a/list_adders.c b/list_adders.c new file mode 100644 index 0000000..87e3be2 --- /dev/null +++ b/list_adders.c @@ -0,0 +1,142 @@ +#include +#include + +LIST *ListCreate(void) { + LIST *new_list; + + new_list = malloc(sizeof(LIST)); + if (!new_list) { + return NULL; + } + + new_list->size = 0; + new_list->current = 0; + new_list->head = 0; + new_list->tail = 0; + + return new_list; +} + +int ListAdd(LIST *list, void *item) { + /* 3 cases + * 1. Empty list + * 2. Insert when cursor is at the tail (& list not empty) + * 3. Insert when cursor is anywhere but the tail (& list not empty) + */ + NODE *new_node = malloc(sizeof(NODE)); + if (new_node == NULL || (list->current == NULL && list->size != 0)) { + return -1; + } + new_node->data = item; + if (list->size == 0) { + list->current = new_node; + list->head = new_node; + list->tail = new_node; + } + else if (list->current == list->tail) { + new_node->previous = list->tail; + list->tail->next = new_node; + list->tail = new_node; + list->current = new_node; + } + else if (list->current != list->tail && list->size > 0) { + new_node->next = list->current->next; + new_node->previous = list->current; + list->current->next->previous = new_node; + list->current->next = new_node; + list->current = new_node; + } + else { + return -1; + } + + list->size++; + return 0; +} + +int ListInsert(LIST *list, void *item) { + /* 3 cases + * 1. Empty list + * 2. Insert when cursor is at the head (& list not empty) + * 3. Insert when cursor is anywhere but the head (& list not empty) + */ + NODE *new_node = malloc(sizeof(NODE)); + if (new_node == NULL || (list->current == NULL && list->size != 0)) { + return -1; + } + + new_node->data = item; + if (list->size == 0) { + list->current = new_node; + list->head = new_node; + list->tail = new_node; + } + else if (list->current == list->head) { + new_node->next = list->head; + list->head->previous = new_node; + list->head = new_node; + list->current = new_node; + } + else if (list->current != list->head && list->size > 0) { + new_node->next = list->current; + new_node->previous = list->current->previous; + list->current->previous->next = new_node; + list->current->previous = new_node; + list->current = new_node; + } + else { + return -1; + } + + list->size++; + return 0; +} + +int ListPrepend(LIST *list, void *item) { + NODE *new_node = malloc(sizeof(NODE)); + if (new_node == NULL) { + return -1; + } + new_node->data = item; + if (list->size == 0) { + list->current = new_node; + list->head = new_node; + list->tail = new_node; + } + new_node->next = list->head; + list->head->previous = new_node; + list->head = new_node; + list->size++; + return 0; + +} + +int ListAppend(LIST *list, void *item) { + NODE *add = malloc(sizeof(NODE)); + if (add == NULL) { + return -1; + } + add->data = item; + if (list->size == 0) { + list->current = add; + list->head = add; + list->tail = add; + } + else { + add->previous = list->tail; + list->tail->next = add; + list->tail = add; + } + list->size++; + return 0; + +} + +void ListConcat(LIST *listone, LIST *listtwo) { + listone->tail->next = listtwo->head; + listone->tail = listtwo->tail; + listone->size += listtwo->size; + free(listtwo); + listtwo = NULL; +} + diff --git a/list_adders.o b/list_adders.o new file mode 100644 index 0000000..f569a99 Binary files /dev/null and b/list_adders.o differ diff --git a/list_movers.c b/list_movers.c new file mode 100644 index 0000000..12c220b --- /dev/null +++ b/list_movers.c @@ -0,0 +1,46 @@ +#include +#include + +int ListCount(LIST *list) { + return (int) list->size; +} + +void *ListFirst(LIST *list) { + if (list->size != 0) { + list->current = list->head; + return list->current->data; + } + return NULL; +} + +void *ListLast(LIST *list) { + if (list->size != 0) { + list->current = list->tail; + return list->current->data; + } + return NULL; +} + +void *ListNext(LIST *list) { + if (list->current->next != NULL) { + list->current = list->current->next; + return list->current->data; + } + return NULL; +} + +void *ListPrev(LIST *list) { + if (list->current->next != NULL) { + list->current = list->current->previous; + return list->current->data; + } + return NULL; +} + +void *ListCurr(LIST *list) { + if (list->current != NULL) { + return list->current->data; + } + return NULL; +} + diff --git a/list_movers.o b/list_movers.o new file mode 100644 index 0000000..bc01d09 Binary files /dev/null and b/list_movers.o differ diff --git a/list_removers.c b/list_removers.c new file mode 100644 index 0000000..3a18fb6 --- /dev/null +++ b/list_removers.c @@ -0,0 +1,82 @@ +#include +#include + +int ListRemove(LIST *list) { + NODE *unused; + if (list->current == NULL) { + return -1; + } + if (list->size == 1) { + unused = list->current; + list->current = NULL; + list->head = NULL; + list->tail = NULL; + } + else if (list->current == list->head) { + unused = list->head; + list->head = list->head->next; + list->current = list->head; + list->current->previous = NULL; + } + else if (list->current == list->tail) { + unused = list->tail; + list->tail= list->tail->previous; + list->current = list->tail; + list->current->next = NULL; + } + else { + unused = list->current; + list->current->previous->next = list->current->next; + list->current->next->previous = list->current->previous; + list->current = list->current->next; + } + + unused->data = NULL; + free(unused->data); + unused->next = NULL; + unused->previous = NULL; + unused = NULL; + free(unused); + list->size--; + return 0; + +} + +void ListFree(LIST *list) { + ListFirst(list); + while (list->head != NULL) { + ListRemove(list); + } + free(list); + +} + +void *ListTrim(LIST *list) { + NODE *tempnode = list->tail->previous; + void *nodedata = list->tail->data; + list->tail->previous = NULL; + free(list->tail); + list->tail = tempnode; + list->size--; + return nodedata; +} + +void *ListSearch(LIST *list, int (*comparator)(void *, void*), + void *comparisonArg) { + ListFirst(list); + while (list->current != NULL) { + if ((*comparator)(list->current->data, comparisonArg)) { + break; + } + else { + ListNext(list); + } + } + + if (list->current == NULL) { + list->current = list->tail; + return NULL; + } + + return list->current; +} diff --git a/list_removers.o b/list_removers.o new file mode 100644 index 0000000..cdba474 Binary files /dev/null and b/list_removers.o differ diff --git a/listtest.c b/listtest.c new file mode 100644 index 0000000..5ff9b00 --- /dev/null +++ b/listtest.c @@ -0,0 +1,255 @@ +#include +#include +#include + +int ListPrint(LIST *list) { + if (ListFirst(list) != NULL) { + printf("%d, ", *(int*)list->current->data); + } + + while (ListNext(list) != NULL) { + printf("%d, ", *(int*)list->current->data); + } + printf("\n"); + return 0; +} + +int main(void) { + LIST *listone = ListCreate(); + LIST *listtwo = ListCreate(); + NODE *cursor, *temp; + int errors = 0; + int one = 1; + int two = 2; + int three = 3; + int four = 4; + int five = 5; + int six = 6; + int seven = 7; + + /* Adders tests */ + if (listone == NULL) { + errors++; + printf("ListCreate failed on list 1\n"); + } + if (listtwo == NULL) { + errors++; + printf("ListCreate failed on list 2\n"); + } + + /* Test each of the three cases for ListAdd */ + ListAdd(listone, &one); + if (ListCount(listone) != 1) { + printf("Error in ListAdd: ListCount expected 1 got %d\n", + ListCount(listone)); + errors++; + } + if (listone->head->data != &one) { + printf("Error in ListAdd: head data unexpected\n"); + errors++; + } + if (listone->tail->data != &one) { + printf("Error in ListAdd: tail data unexpected\n"); + errors++; + } + if (listone->current->data != &one) { + printf("Error in ListAdd: current data unexpected\n"); + errors++; + } + + /* ListAdd 2 */ + ListAdd(listone, &two); + if (ListCount(listone) != 2) { + printf("Error in ListAdd 2: ListCount expected 2 got %d\n", + ListCount(listone)); + errors++; + } + if (listone->tail->data != &two) { + printf("Error in ListAdd 2: tail data unexpected\n"); + errors++; + } + if (listone->current->data != &two) { + printf("Error in ListAdd 2: current data unexpected\n"); + errors++; + } + + /* ListAdd 3 */ + ListFirst(listone); + if (listone->current != listone->head) { + printf("Error in ListAdd 3: ListFirst didn't move cursor\n"); + errors++; + } + ListAdd(listone, &three); + if (ListCount(listone) != 3) { + printf("Error in ListAdd 3: ListCount expected 3 got %d\n", + ListCount(listone)); + errors++; + } + if (listone->head->data != &one) { + printf("Error in ListAdd 3: head data unexpected\n"); + errors++; + } + if (listone->tail->data != &two) { + printf("Error in ListAdd 3: tail data unexpected\n"); + errors++; + } + if (listone->current->data != &three) { + printf("Error in ListAdd 3: current data unexpected\n"); + errors++; + } + + ListLast(listone); + if (listone->current != listone->tail) { + printf("Error: ListLast didn't move cursor\n"); + errors++; + } + + /* List Append */ + ListFirst(listone); + cursor = listone->current; + ListAppend(listone, &four); + if (ListCount(listone) != 4) { + printf("Error in ListAppend: ListCount expected 4 got %d\n", + ListCount(listone)); + errors++; + } + if (listone->tail->data != &four) { + printf("Error in ListAppend: tail data unexpected\n"); + errors++; + } + if (listone->current != cursor) { + printf("Error in ListAppend: Cursor moved"); + errors++; + } + + ListNext(listone); + if (listone->current->data != &three) { + printf("Error: ListNext didn't move the cursor\n"); + errors++; + } + + /* List Prepend */ + cursor = listone->current; + ListPrepend(listone, &five); + if (ListCount(listone) != 5) { + printf("Error in ListPrepend: ListCount expected 5 got %d\n", + ListCount(listone)); + errors++; + } + if (listone->head->data != &five) { + printf("Error in ListPrepend: head data unexpected\n"); + errors++; + } + if (listone->current != cursor) { + printf("Error in ListPrepend: Cursor moved"); + errors++; + } + + temp = listone->current->previous; + ListPrev(listone); + if (temp != listone->current) { + printf("Error: ListPrev did not function properly\n"); + errors++; + } + + /* List Insert */ + /* Test w/ cursor at head of list */ + ListFirst(listone); + temp = listone->current; + ListInsert(listone, &six); + + if (listone->head->data != &six) { + printf("Error in ListInsert: Did not change head of list\n"); + errors++; + } + + if (ListCount(listone) != 6) { + printf("Error: ListInsert did not update count\n"); + errors++; + } + + /* Move cursor and test */ + ListNext(listone); + ListNext(listone); + + ListInsert(listone, &seven); + + if (ListCount(listone) != 7) { + printf("Error: ListInsert did not update count\n"); + errors++; + } + if (listone->current->data != &seven) { + printf("Error: ListInsert did not update current\n"); + errors++; + } + if (ListCurr(listone) != listone->current->data) { + printf("Error: ListCurr did not return current data\n"); + errors++; + } + + ListAdd(listtwo, &one); + ListAdd(listtwo, &two); + ListAdd(listtwo, &three); + ListAdd(listtwo, &four); + ListAdd(listtwo, &five); + + ListFirst(listtwo); + ListNext(listtwo); + ListNext(listtwo); + if (ListCurr(listtwo) != &three) { + printf("Error: List movers\n"); + errors++; + } + + /* Test remove */ + + ListRemove(listtwo); + if (ListCurr(listtwo) != &four) { + printf("Error: ListRemove did not move the cursor properly\n"); + errors++; + } + if (ListCount(listtwo) != 4) { + printf("Error: ListRemove did not update count\n"); + errors++; + } + + ListFirst(listtwo); + ListRemove(listtwo); + if (listtwo->head->data != &two || listtwo->current->data != &two) { + printf("Error: ListRemove did not change the head or cursor\n"); + errors++; + } + + ListLast(listtwo); + ListRemove(listtwo); + if (listtwo->tail->data != &four || listtwo->current->data != &four) { + printf("Error: ListRemove did not change the tail or cursor\n"); + errors++; + } + + /* Test concat */ + ListConcat(listone, listtwo); + listtwo = NULL; + if (ListCount(listone) != 9) { + printf("Error: ListConcat did not change list size\n"); + errors++; + } + if (listone->tail->previous->data != &two || listone->tail->data != &four) { + printf("Error: ListConcat did not concatenate properly\n"); + errors++; + } + + /* Tested free in GDB */ + ListTrim(listone); + if (listone->tail->data != &two) { + printf("Error: ListTrim did not set tail properly\n"); + errors++; + } + if (ListCount(listone) != 8) { + printf("Error: ListTrim did not change count properly\n"); + errors++; + } + printf("Finished with %d errors\n", errors); + + return 0; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..1752b83 --- /dev/null +++ b/main.c @@ -0,0 +1,240 @@ +/* + * A simple chat program by Dane MacFadden + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXBUFSIZE 30 +#define BACKLOG 10 + +LIST *receivelist, *sendlist; +pthread_mutex_t sendmut, receivemut; +int sockfd, confd; + +void *input(void *arg) { + char msg[30]; + char *copy = malloc(31); + while (1) { + printf("Type a message: "); + fgets(msg, sizeof(msg), stdin); + strcpy(copy, msg); + pthread_mutex_lock(&sendmut); + ListAppend(sendlist, copy); + pthread_mutex_unlock(&sendmut); + } + return 0; +} + +void *sender(void *arg) { + char msg[30]; + while (1) { + pthread_mutex_lock(&sendmut); + if (ListCount(sendlist) != 0) { + ListFirst(sendlist); + strcpy(msg, ListCurr(sendlist)); + ListRemove(sendlist); + } + else { + pthread_mutex_unlock(&sendmut); + continue; + } + pthread_mutex_unlock(&sendmut); + } + return 0; +} + +void *receiver(void *arg) { + char msg[30]; + char *copy = malloc(31); + while (1) { + recv(sockfd, msg, 30, 0); + if (msg[0] != '\n' && msg[0] != '\0') { + pthread_mutex_lock(&receivemut); + ListAppend(receivelist, msg); + pthread_mutex_unlock(&receivemut); + } + } + return 0; +} + +void *output(void *arg) { + char *msg; + while (1) { + pthread_mutex_lock(&receivemut); + if (ListCount(receivelist) != 0) { + ListFirst(receivelist); + msg = ListCurr(receivelist); + ListRemove(receivelist); + printf("%s\n", (char*)msg); + } + pthread_mutex_unlock(&receivemut); + } + + return 0; +} + +void *get_in_addr(struct sockaddr *sa) { + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +int main(int argc, char* argv[]) { + pthread_t input_thread, send_thread, receive_thread, output_thread; + socklen_t sin_size; + struct addrinfo *p, *q; + struct addrinfo hints; + struct addrinfo *servinfo; + struct sockaddr_storage their_addr; + char s[INET6_ADDRSTRLEN]; + char *remote_machine, *local_port, *remote_port; + int status; + int yes = 1; + + if (argc != 4) { + printf("Wrong number of arguments. Usage: ./chatty \ + \n"); + return -1; + } + + if (pthread_mutex_init(&sendmut, NULL) != 0) { + printf("Error: send mutex init failed\n"); + return -1; + } + if (pthread_mutex_init(&receivemut, NULL) != 0) { + printf("Error: receive mutex init failed\n"); + return -1; + } + + local_port = argv[1]; + remote_machine = argv[2]; + remote_port = argv[3]; + + receivelist = ListCreate(); + sendlist = ListCreate(); + + if (atoi(local_port) < atoi(remote_port)) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if ((status = getaddrinfo(NULL, local_port, &hints, &servinfo)) != 0) { + fprintf(stderr,"getaddrinfo error: %s\n",gai_strerror(status)); + exit(1); + } + + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, + p->ai_socktype, + p->ai_protocol)) == -1) { + perror("Server: socket"); + continue; + } + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(int)) == -1) { + perror("setsockopt"); + exit(1); + } + if (bind(sockfd, servinfo->ai_addr, servinfo->ai_addrlen) == -1) { + close(sockfd); + perror("Server: bind"); + continue; + } + + break; + } + if (p == NULL) { + fprintf(stderr, "Server: failed to bind socket\n"); + return 2; + } + else { + servinfo = p; + } + + freeaddrinfo(servinfo); + if (listen(sockfd, BACKLOG) == -1) { + perror("listen"); + exit(1); + } + + sin_size = sizeof their_addr; + printf("Waiting for connection...\n"); + confd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); + if (confd == -1) { + perror("accept"); + } + inet_ntop(their_addr.ss_family, + get_in_addr((struct sockaddr *)&their_addr), + s, + sizeof s); + printf("Getting chatty with %s\n", s); + } + else { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + if ((status = getaddrinfo(remote_machine, remote_port, &hints, + &servinfo)) != 0) { + fprintf(stderr,"getaddrinfo error: %s\n",gai_strerror(status)); + exit(1); + } + for (q = servinfo; q != NULL; q = q->ai_next) { + if ((sockfd = socket(q->ai_family, + q->ai_socktype, + q->ai_protocol)) == -1) { + perror("Client: socket"); + continue; + } + inet_ntop(q->ai_family, get_in_addr((struct sockaddr *)q->ai_addr), + s, sizeof s); + printf("Client: attempting connection to %s\n", s); + + if (connect(sockfd, q->ai_addr, q->ai_addrlen) == -1) { + perror("Client: connect"); + close(sockfd); + continue; + } + break; + } + + if (q == NULL) { + fprintf(stderr, "Client: failed to create socket\n"); + return 2; + } + else { + servinfo = q; + } + printf("Connected!\n"); + } + pthread_create(&input_thread, NULL, input, NULL); + pthread_create(&send_thread, NULL, sender, NULL); + pthread_create(&receive_thread, NULL, receiver, NULL); + pthread_create(&output_thread, NULL, output, NULL); + pthread_join(input_thread, NULL); + pthread_join(send_thread, NULL); + pthread_join(receive_thread, NULL); + pthread_join(output_thread, NULL); + + close(sockfd); + close(confd); + return 0; +} + diff --git a/main.o b/main.o new file mode 100644 index 0000000..3e775c2 Binary files /dev/null and b/main.o differ