merge fix

This commit is contained in:
Dane MacFadden
2025-07-25 19:06:44 -06:00
commit 1e7bf3f5e0
14 changed files with 947 additions and 0 deletions
+25
View File
@@ -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
+6
View File
@@ -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.
Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+151
View File
@@ -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
+142
View File
@@ -0,0 +1,142 @@
#include <list.h>
#include <stdlib.h>
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;
}
BIN
View File
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
#include <list.h>
#include <stddef.h>
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;
}
BIN
View File
Binary file not shown.
+82
View File
@@ -0,0 +1,82 @@
#include <list.h>
#include <stdlib.h>
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;
}
BIN
View File
Binary file not shown.
+255
View File
@@ -0,0 +1,255 @@
#include <list.h>
#include <stdio.h>
#include <stdlib.h>
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;
}
+240
View File
@@ -0,0 +1,240 @@
/*
* A simple chat program by Dane MacFadden
*
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <list.h>
#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 <local port> \
<remote IP> <remote port>\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;
}
BIN
View File
Binary file not shown.