Skip to content

Commit 49559d8

Browse files
authored
Merge pull request #17 from vainiovano/no-fork
Use poll instead of multiple processes
2 parents 6555234 + dd3410e commit 49559d8

File tree

1 file changed

+115
-78
lines changed

1 file changed

+115
-78
lines changed

kirc.c

Lines changed: 115 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
#include <stdlib.h>
99
#include <string.h>
1010
#include <fcntl.h>
11-
#include <sys/select.h>
12-
#include <sys/wait.h>
1311
#include <termios.h>
12+
#include <poll.h>
13+
#include <errno.h>
1414

1515
#define MSG_MAX 512 /* guaranteed max message length */
1616
#define CHA_MAX 200 /* gauranteed max channel length */
@@ -42,20 +42,6 @@ log_append(char *str, char *path) {
4242
fclose(out);
4343
}
4444

45-
static int
46-
kbhit(void) {
47-
48-
int byteswaiting;
49-
struct timespec ts = {0};
50-
fd_set fds;
51-
52-
FD_ZERO(&fds);
53-
FD_SET(0, &fds);
54-
byteswaiting = pselect(1, &fds, NULL, NULL, &ts, NULL);
55-
56-
return byteswaiting > 0;
57-
}
58-
5945
static void
6046
raw(char *fmt, ...) {
6147

@@ -154,73 +140,84 @@ raw_parser(char *usrin) {
154140
} else printw("%*s\x1b[33;1m%-.*s\x1b[0m %s", s, "", g, nickname, message);
155141
}
156142

157-
static void
158-
parent_process(int fd[], pid_t pid) {
159-
char usrin[MSG_MAX], v1[MSG_MAX - CHA_MAX], v2[CHA_MAX], c1;
160-
struct termios tp, save;
143+
static char message_buffer[MSG_MAX + 1];
144+
/* The first character after the end */
145+
static size_t message_end = 0;
161146

162-
tcgetattr(STDIN_FILENO, &tp);
163-
save = tp;
164-
tp.c_cc[VERASE] = 127;
165-
166-
if (tcsetattr(STDIN_FILENO, TCSANOW, &tp) < 0) exit(2);
167-
168-
while (waitpid(pid, NULL, WNOHANG) == 0) {
169-
if (!kbhit()) dprintf(fd[1], "/\n");
170-
else if (fgets(usrin, MSG_MAX, stdin) != NULL &&
171-
(sscanf(usrin, "/%[m] %s %[^\n]\n", &c1, v2, v1) > 2 ||
172-
sscanf(usrin, "/%[xMQqnjp] %[^\n]\n", &c1, v1) > 0)) {
173-
switch (c1) {
174-
case 'x': dprintf(fd[1], "%s\n", v1); break;
175-
case 'q': dprintf(fd[1], "quit\n"); break;
176-
case 'Q': dprintf(fd[1], "quit %s\n", v1); break;
177-
case 'j': dprintf(fd[1], "join %s\n", v1); break;
178-
case 'p': dprintf(fd[1], "part %s\n", v1); break;
179-
case 'n': dprintf(fd[1], "names #%s\n", chan); break;
180-
case 'M': dprintf(fd[1], "privmsg nickserv :%s\n", v1); break;
181-
case 'm': dprintf(fd[1], "privmsg %s :%s\n", v2, v1); break;
147+
static int
148+
handle_server_message(void) {
149+
for (;;) {
150+
ssize_t sl = read(conn, &message_buffer[message_end], MSG_MAX - message_end);
151+
if (sl == -1) {
152+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
153+
/* Let's wait for new input */
154+
return 0;
155+
} else {
156+
perror("read");
157+
return -2;
182158
}
183-
} else dprintf(fd[1], "privmsg #%s :%s", chan, usrin);
184-
}
185-
186-
if (tcsetattr(STDIN_FILENO, TCSANOW, &save) < 0) exit(2);
187-
188-
perror("Connection closed");
189-
}
190-
191-
static void
192-
child_process(int fd[]) {
193-
int sl, i, o = 0;
194-
char u[MSG_MAX], s, b[MSG_MAX];
195-
196-
irc_init();
159+
}
160+
if (sl == 0) {
161+
fputs("Connection closed\n", stderr);
162+
return -1;
163+
}
197164

198-
raw("NICK %s\r\n", nick);
199-
raw("USER %s - - :%s\r\n", (user ? user : nick), (real ? real : nick));
165+
size_t old_message_end = message_end;
166+
message_end += sl;
200167

201-
if (pass) raw("PASS %s\r\n", pass);
202-
if (inic) raw("%s\r\n", inic);
168+
/* Iterate over the added part to find \r\n */
169+
for (size_t i = old_message_end; i < message_end; ++i) {
170+
if (i != 0 && message_buffer[i - 1] == '\r' && message_buffer[i] == '\n') {
171+
/* Cut the buffer here for parsing */
172+
char saved_char = message_buffer[i + 1];
173+
message_buffer[i + 1] = '\0';
174+
raw_parser(message_buffer);
175+
message_buffer[i + 1] = saved_char;
203176

204-
while ((sl = read(conn, &s, 1))) {
205-
if (sl > 0) b[o] = s;
177+
/* Move the part after the parsed line to the beginning */
178+
memmove(&message_buffer, &message_buffer[i + 1], message_end - i - 1);
206179

207-
if ((o > 0 && b[o - 1] == '\r' && b[o] == '\n') || o == MSG_MAX) {
208-
b[o + 1] = '\0';
209-
raw_parser(b);
210-
o = 0;
211-
} else if (sl > 0) ++o;
180+
/* There might still be other lines to be read */
181+
message_end = message_end - i - 1;
182+
i = 0;
183+
}
184+
}
185+
if (message_end == MSG_MAX) {
186+
/* The buffer is full and doesn't contain \r\n */
187+
message_end = 0;
188+
}
189+
}
190+
}
212191

213-
if (read(fd[0], u, MSG_MAX) > 0) {
214-
for (i = 0; u[i] != '\n'; ++i) continue;
215-
if (u[0] != '/') raw("%-*.*s\r\n", i, i, u);
192+
static void
193+
handle_user_input(void) {
194+
char usrin[MSG_MAX], v1[MSG_MAX - CHA_MAX], v2[CHA_MAX], c1;
195+
if (fgets(usrin, MSG_MAX, stdin) != NULL &&
196+
(sscanf(usrin, "/%[m] %s %[^\n]\n", &c1, v2, v1) > 2 ||
197+
sscanf(usrin, "/%[xMQqnjp] %[^\n]\n", &c1, v1) > 0)) {
198+
switch (c1) {
199+
case 'x': raw("%s\r\n", v1); break;
200+
case 'q': raw("quit\r\n"); break;
201+
case 'Q': raw("quit %s\r\n", v1); break;
202+
case 'j': raw("join %s\r\n", v1); break;
203+
case 'p': raw("part %s\r\n", v1); break;
204+
case 'n': raw("names #%s\r\n", chan); break;
205+
case 'M': raw("privmsg nickserv :%s\r\n", v1); break;
206+
case 'm': raw("privmsg %s :%s\r\n", v2, v1); break;
216207
}
208+
} else {
209+
size_t msg_len = strlen(usrin);
210+
/* Remove the trailing newline to add a carriage return */
211+
if (usrin[msg_len - 1] == '\n')
212+
usrin[msg_len - 1] = '\0';
213+
raw("privmsg #%s :%s\r\n", chan, usrin);
217214
}
218215
}
219216

220217
int
221218
main(int argc, char **argv) {
222219

223-
int fd[2], cval;
220+
int cval;
224221

225222
while ((cval = getopt(argc, argv, "s:p:o:n:k:c:u:r:x:w:W:hvV")) != -1) {
226223
switch (cval) {
@@ -247,16 +244,56 @@ main(int argc, char **argv) {
247244
return 1;
248245
}
249246

250-
if (pipe(fd) < 0) {
251-
perror("Pipe() failed");
252-
return 1;
253-
}
247+
irc_init();
248+
249+
raw("NICK %s\r\n", nick);
250+
raw("USER %s - - :%s\r\n", (user ? user : nick), (real ? real : nick));
251+
252+
if (pass) raw("PASS %s\r\n", pass);
253+
if (inic) raw("%s\r\n", inic);
254+
255+
struct termios tp, save;
256+
257+
tcgetattr(STDIN_FILENO, &tp);
258+
save = tp;
259+
tp.c_cc[VERASE] = 127;
260+
261+
if (tcsetattr(STDIN_FILENO, TCSANOW, &tp) < 0) return 2;
262+
263+
struct pollfd fds[2];
264+
fds[0].fd = STDIN_FILENO;
265+
fds[1].fd = conn;
266+
fds[0].events = POLLIN;
267+
fds[1].events = POLLIN;
268+
269+
int return_code = 0;
254270

255-
pid_t pid = fork();
271+
for (;;) {
272+
int poll_res = poll(fds, 2, -1);
256273

257-
switch (pid) {
258-
case -1 : perror("Fork() failed"); return 1;
259-
case 0 : child_process(fd); return 0;
260-
default : parent_process(fd, pid); return 0;
274+
if (poll_res != -1) {
275+
if (fds[0].revents & POLLIN) {
276+
handle_user_input();
277+
}
278+
279+
if (fds[1].revents & POLLIN) {
280+
int rc = handle_server_message();
281+
/* Has the server closed the connection? */
282+
if (rc != 0) {
283+
if (rc == -2) return_code = EXIT_FAILURE;
284+
goto end;
285+
};
286+
}
287+
} else {
288+
if (errno == EAGAIN) continue;
289+
perror("poll");
290+
return_code = EXIT_FAILURE;
291+
goto end;
292+
}
261293
}
294+
295+
end:
296+
if (tcsetattr(STDIN_FILENO, TCSANOW, &save) < 0) return 2;
297+
298+
return return_code;
262299
}

0 commit comments

Comments
 (0)