-
Notifications
You must be signed in to change notification settings - Fork 3
/
chatroom.c
155 lines (117 loc) · 3.68 KB
/
chatroom.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include "chatroom.h"
#include "user.h"
#define MAX_ROOMS 20
#define MAX_ROOMNAME_LENGTH 20
#define MAX_USERS_PER_ROOM 20
struct chatroom {
bool in_use;
char name[MAX_ROOMNAME_LENGTH + 1];
int user_indices[MAX_USERS_PER_ROOM];
};
static struct chatroom rooms[MAX_ROOMS];
/* Semaphore used to synchronize access to the chatrooms table. */
static sem_t rooms_sema;
/* Initializes any data structures for the chat rooms. */
bool chatroom_init(void) {
int i, j;
/* Initialize the semaphore to allow one thread to access the table
at a time. */
if (sem_init(&rooms_sema, 0, 1) == -1) {
perror("sem_init");
return false;
}
/* For each room, mark it not in use and set all of the
user indices to -1. */
for (i = 0; i < MAX_ROOMS; ++i) {
rooms[i].in_use = false;
rooms[i].name[0] = 0;
for (j = 0; j < MAX_USERS_PER_ROOM; ++j) {
rooms[i].user_indices[j] = -1;
}
}
return true;
}
/* Adds a user to an existing chatroom or creates a new one if it doesn't exist.
Returns false on error and fills the error_message buffer with a description
of the problem. */
bool chatroom_join(int user_index, const char *room_name,
char *error_message, int error_buffer_size) {
int i, j;
/* Verify that the room name is valid (not null and less than max. */
if (room_name == 0 || strlen(room_name) > MAX_ROOMNAME_LENGTH) {
snprintf(error_message, error_buffer_size,
"must specify a room name (max: %d chars)", MAX_ROOMNAME_LENGTH);
return false;
}
/* Check that the user index is valid. */
if (user_index == INVALID_USER) {
strncpy(error_message, "user not logged in", error_buffer_size);
error_message[error_buffer_size - 1] = 0;
return false;
}
/* Lock the chatrooms table. */
sem_wait(&rooms_sema);
/* Search through the table to see if a room with the given name already exists. */
for (i = 0; i < MAX_ROOMS; ++i) {
if (strcmp(rooms[i].name, room_name) == 0) {
/* The room was found, now add this user. */
for (j = 0; j < MAX_USERS_PER_ROOM; ++j) {
/* TODO: check if user was already added. */
if (rooms[i].user_indices[j] == INVALID_USER) {
rooms[i].user_indices[j] = user_index;
break;
}
}
break;
}
}
/* If the room wasn't found, create it. */
if (i == MAX_ROOMS) {
/* Find a room that isn't in use. */
for (i = 0; i < MAX_ROOMS; ++i) {
if (!rooms[i].in_use) {
rooms[i].in_use = true;
strcpy(rooms[i].name, room_name);
/* Clear out any existing users, and then add this
user as the first entry. */
for (j = 0; j < MAX_USERS_PER_ROOM; ++j) {
rooms[i].user_indices[j] = INVALID_USER;
}
rooms[i].user_indices[0] = user_index;
break;
}
}
}
/* Check if a room was found. */
if (i == MAX_ROOMS) {
strncpy(error_message, "no space available to create chatroom", error_buffer_size);
error_message[error_buffer_size - 1] = 0;
sem_post(&rooms_sema);
return false;
}
/* Release the lock. */
sem_post(&rooms_sema);
return true;
}
/* Removes a user from the given chatroom (if it exists). */
void chatroom_leave(int user_index, const char *room_name) {
int i, j;
/* Lock the chatrooms table. */
sem_wait(&rooms_sema);
/* Find the room name in the chatrooms table. */
for (i = 0; i < MAX_ROOMS; ++i) {
if (strcmp(rooms[i].name, room_name) == 0) {
/* Room found, now remove the user index from the table. */
for (j = 0; j < MAX_USERS_PER_ROOM; ++j) {
if (rooms[i].user_indices[j] == user_index) {
rooms[i].user_indices[j] = INVALID_USER;
}
}
}
}
/* Release the lock. */
sem_post(&rooms_sema);
}