wm: ticl

Download patch

ref: abeb6b0542e9b7db6ce1ab102f03fc7499f54eb2
parent: e09ff804102de75826409b6043619d2668d9cef8
author: libredev <libredev@ircforever.org>
date: Wed Nov 23 11:36:01 EST 2022

replaced poll with picoev (event loop library)

git/query: bad hash ff85d9ef578842a40f7c91d2544b7932cec74b9d
--- /dev/null
+++ b/.gitmodules
@@ -1,0 +1,3 @@
+[submodule "picoev"]
+	path = picoev
+	url = https://github.com/kazuho/picoev
--- a/Makefile
+++ b/Makefile
@@ -6,9 +6,10 @@
 CC     = cc
 CFLAGS = -g -std=c89 -Wall -Wextra -pedantic -Wfatal-errors -Wconversion\
 	 -Wstrict-prototypes -Wold-style-definition\
-	 -D_POSIX_C_SOURCE=200809L
+	 -D_POSIX_C_SOURCE=200809L -isystem ./picoev/
 #CFLAGS += -fsanitize=address -fno-omit-frame-pointer
 #LDFLAGS = -fsanitize=address
+LDFLAGS =  -L ./picoev/ -l picoev
 
 VERSION != date '+%Y-%m-%d'
 PROGRAM = ticl
@@ -16,8 +17,11 @@
 HEADERS = htable.h util.c
 OBJECTS = $(SOURCES:.c=.o)
 
-all: clean $(PROGRAM)
+all: clean libpicoev.a $(PROGRAM)
 
+libpicoev.a:
+	cd picoev && $(MAKE) libpicoev.a CC=gcc LINUX_BUILD=1 CC_DEBUG_FLAGS=-g
+
 $(PROGRAM): $(OBJECTS)
 	$(CC) -o $@ $(OBJECTS) $(LDFLAGS)
 
@@ -26,6 +30,7 @@
 
 clean:
 	rm -f $(PROGRAM) $(OBJECTS) $(PROGRAM)-$(VERSION).tar.gz
+	cd picoev && $(MAKE) clean
 
 dist: clean
 	mkdir -p $(PROGRAM)-$(VERSION)
--- a/main.c
+++ b/main.c
@@ -3,7 +3,6 @@
  * See COPYING file for more information.
  */
 
-#include <fcntl.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -12,20 +11,23 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <picoev.h>
+
 #include "htable.h"
 #include "util.c"
 
 #define BUFFER_LEN	1024
-#define MAX_NICK_LEN	16
+#define NICK_LEN	16
 
-#define POLLFD_ADDEND	100
+#define EVENT_ADDEND	10000
 #define NET_ADDEND	10
 #define USER_ADDEND	100
+#define EVENT_TIMEOUT	10000
 
-#define TIME_OUT	180
+#define UNUSED(x) (void)(x)
 
-typedef struct {
-	int	 id;	/* fd of linker	*/
+struct network {
+	int	 id;	/* event index	*/
 	int	 join;	/* is joined	*/
 	char	*name;	/* name		*/
 	char	*symb;	/* symbol	*/
@@ -32,46 +34,51 @@
 	char	*host;	/* hostname	*/
 	char	*port;	/* port		*/
 	char	*chan;	/* channel	*/
-} Network;
+};
 
-/* user defined data for each fd */
-struct FdData {
+struct event {
+	int	 fd;		/* event fd	*/
 	int	 netid;		/* net index	*/
-	int	 suffix;	/* suffix count	*/
 	char	*user;		/* user nick	*/
-	time_t	 time;
+	int	 suffix;	/* suffix count	*/
 };
 
-static struct pollfd	*fdset;		/* pollfd set		*/
-static struct FdData	*fddata;	/* user data set	*/
-static int		 fdslen;	/* fdset length	*/
-static int		 fdscap;	/* fdset capacity	*/
+static struct event	*events;	/* events array	  */
+static int		 evlen;		/* array length	  */
+static int		 evcap;		/* array capacity */
 
-static Network	*networks;	/* linked list of networks	*/
-static int	 netlen;	/* current network length	*/
-static int	 netcap;	/* total memory allocated	*/
-static Htable	*users;		/* users-clones hash table	*/
+static struct network	*networks;	/* networks array */
+static int		 netlen;	/* array length	  */
+static int		 netcap;	/* array capacity */
 
-char		msg[BUFFER_LEN];
-static int	isrunning = 1;
+/*
+ * hash table of users
+ * key   -> <user_nick> + '[' + <network_symbol> + ']'
+ * value -> array of all user's clones event ids indexed
+ *	    corresponding to its connected network
+ */
+static struct Htable	*users;		
 
+static char	msg [BUFFER_LEN];
+static int	done;
+
 /* functions prototype */
-void	 handle_fifo_input(int, char *);
-void	 handle_server_output(int);
-void	 net_add(char *, char *, char *, char *, char *);
-void	 net_del(char *);
+void	 fifo_event_cb(picoev_loop *, int, int, void *);
+void	 server_event_cb(picoev_loop *, int, int, void *);
+void	 net_add(picoev_loop *, char *, char *, char *, char *, char *);
+void	 net_del(picoev_loop *, char *);
 void	 net_del_raw(int);
-void	 net_update(int);
-void	 user_add(char *, int);
-void	 user_del(char *, char *);
-int	*user_ids(char *, int);
-int	*euserids(char *);
-int	 clone_add(char *, int);
-int	 fdset_add(int, int, int, char *);
-void	 fdset_del(int);
+void	 net_update(picoev_loop *, int);
+void	 user_add(picoev_loop *, char *, int);
+void	 user_del(picoev_loop *, char *, char *);
+int	*user_get_ids(char *);
+int	*clone_get_user_ids(char *, int);
+int	 clone_add(picoev_loop *, char *, int);
+int	 event_add(picoev_loop *, int, int, char *);
+void	 event_del(picoev_loop *, int);
 void   	 nick_add_symb(char *, int);
 void   	 privmsg_update(char *, char *, int);
-void   	 terminate(int);
+void   	 clean_exit(picoev_loop *, int);
 void   	 print_table(void);
 void   	 print_htable(void);
 void   	 print_users(void);
@@ -80,8 +87,8 @@
 int
 main(int argc, char *argv[])
 {
-	int i, n, timeout_id;
-	time_t last_time, timeout;
+	picoev_loop *loop;
+	int fd;
 
 	/* set stdout to unbufferd */
 	setvbuf(stdout, NULL, _IONBF, 0);
@@ -93,88 +100,52 @@
 	}
 
 	/* init global variables */
-	fdscap = POLLFD_ADDEND;
-	fdset = ecalloc((size_t)fdscap, sizeof(struct pollfd));
-	fddata = ecalloc((size_t)fdscap, sizeof(struct FdData));
+	evcap = EVENT_ADDEND;
 	netcap = NET_ADDEND;
-	networks = ecalloc((size_t)netcap, sizeof(Network));
-	/*
-	 * hash table of users
-	 * key   -> <nickname> + '[' + <network_symbol> + ']'
-	 * value -> array of linked clones fds indexed
-	 *	    corresponding to its connected network
-	 */
+	events = ecalloc((size_t)evcap, sizeof(struct event));
+	networks = ecalloc((size_t)netcap, sizeof(struct network));
 	users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND);
 
-	/* add fifo_fd */
-	fdset_add(fifo_open(argv[1]), -1, -1, NULL);
+	/* init picoev */
+	picoev_init(1000);
+	/* create loop */
+	loop = picoev_create_loop(60);
+	/* get fifo fd */
+	fd = fifo_open(argv[1]);
+	/* add fifo fd */
+	picoev_add(loop, fd, PICOEV_READ, 0, fifo_event_cb, argv[1]);
+	/* loop */
+	while (!done)
+		picoev_loop_once(loop, 10);
+	/* clean and exit */
+	clean_exit(loop, 0);
 
-	/* select loop */
-	while (isrunning) {
-		/* calculate timeout from fdset */
-		last_time = time(NULL);
-		for (i = 0; i < fdslen; i++) {
-			if ((fddata[i].netid != -1)	/* ignore fifo */
-			&& (fddata[i].time < last_time)) {
-				last_time = fddata[i].time;
-				timeout_id = i;
-			}
-		}
-		timeout = TIME_OUT - (time(NULL) - last_time);
-		if (timeout < 0)
-			timeout = 0;
-
-		n = poll(fdset, (nfds_t)fdslen, (int)timeout * 1000);
-		if (n == -1) {
-			printf("error: poll: %s\n", strerror(errno));
-			terminate(1);
-		} else if (n == 0) {
-			snprintf(msg, sizeof(msg), "PING :%s\r\n",
-				networks[fddata[timeout_id].netid].host);
-			writeall(fdset[timeout_id].fd, msg);
-			fddata[timeout_id].time = time(NULL);
-			continue;
-		}
-		for (i = 0; i < fdslen; i++) {
-			if (!(fdset[i].revents & (POLLIN|POLLHUP)))
-				continue;
-			if (fddata[i].netid == -1)
-				handle_fifo_input(i, argv[1]);
-			else
-				handle_server_output(i);
-
-			fddata[i].time = time(NULL);
-
-			/*
-			 * handle one ready fd at one time because if we
-			 * close upcoming ready fd, it cause infinite loop.
-			 */
-			break;
-		}
-	}
-	terminate(0);
 	return 0;
 }
 
 void
-handle_fifo_input(int id, char *path)
+fifo_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg)
 {
-	char buffer[BUFFER_LEN];
-	char *buf;
-	char *cmd;
-	ssize_t n;
+	char	 buffer[BUFFER_LEN];
+	char	*buf;
+	char	*cmd;
+	ssize_t	 n;
 
-	/* if failed to read data */
-	if ((n = readline(fdset[id].fd, buffer, sizeof(buffer))) < 1) {
-		if (n == 0) {	/* restart again */
-			fdset_del(id);
-			fdset_add(fifo_open(path), -1, -1, NULL);
-		} else if ((errno != EAGAIN) && (errno != EINTR)) {
-			printf("error: %d: read: %s\n", fdset[id].fd, strerror(errno));
-		}
+	UNUSED(revents);
+
+	n = readline(fd, buffer, sizeof(buffer));
+	if (n == -1) {
+		if (errno == EAGAIN || errno == EWOULDBLOCK)
+			return;
+		printf("error: %d: read: %s\n", fd, strerror(errno));
+		clean_exit(loop, 1);
+	} else if (n == 0) {	/* reopen fifo again */
+		picoev_del(loop, fd);
+		close(fd);
+		fd = fifo_open((char *)cb_arg);
+		picoev_add(loop, fd, PICOEV_READ, 0, fifo_event_cb, cb_arg);
 		return;
 	}
-	/* printf("fifo: %s\n", buffer); */
 
 	buf = buffer;
 	cmd = split(&buf, ' ');
@@ -187,13 +158,13 @@
 		if (!*name || !*symb || !*host || !*port || !*chan)
 			printf("usage: netadd <name> <symbol> <hostname> <port> <channel>\n");
 		else
-			net_add(name, symb, host, port, chan);
+			net_add(loop, name, symb, host, port, chan);
 	} else if (strcmp(cmd, "netdel") == 0) {
 		char *name = buf;
 		if (!*name)
 			printf("usage: netdel <name>\n");
 		else
-			net_del(name);
+			net_del(loop, name);
 	} else if (strcmp(cmd, "print") == 0) {
 		print_table();
 	} else if (strcmp(cmd, "htable") == 0) {
@@ -201,7 +172,7 @@
 	} else if (strcmp(cmd, "users") == 0) {
 		print_users();
 	} else if (strcmp(cmd, "exit") == 0) {
-		isrunning = 0;
+		done = 1;
 	} else {
 		printf("error: %s is not a command\n", cmd);
 	}
@@ -208,32 +179,46 @@
 }
 
 void
-handle_server_output(int id)
+server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg)
 {
-	char buffer	[BUFFER_LEN];
-	char backup	[BUFFER_LEN];
-	char linker_nick [MAX_NICK_LEN];
-	char *buf;
-	char *cmd;
-	char *nick;
-	int i, netid;
-	ssize_t n;
+	char	 buffer	[BUFFER_LEN];
+	char	 backup	[BUFFER_LEN];
+	char 	 lnick	[NICK_LEN];	/* linker nick */
+	char 	*buf;
+	char 	*cmd;
+	char 	*nick;
+	int	 i, id, netid;
+	ssize_t	 n;
 
-	/* if failed to read data */
-	if ((n = readline(fdset[id].fd, buffer, sizeof(buffer))) < 1) {
-		if (n == 0) {
-			printf("error: %d: connection closed\n", fdset[id].fd);
-			printf("ping: %d : %ld\n", fdset[id].fd, time(0) - fddata[id].time);
-			terminate(1);
-			/* fdset_del(fdset[id].fd); */
-		} else if ((errno != EAGAIN) && (errno != EINTR)) {
-			printf("error: %d: read: %s\n", fdset[id].fd, strerror(errno));
-			printf("ping: %d : %ld\n", fdset[id].fd, time(0) - fddata[id].time);
-			terminate(1);
+	id = (int)((struct event *)cb_arg - events);
+	netid = events[id].netid;
+
+	if ((revents & PICOEV_WRITE) != 0) {
+		if (events[id].user == NULL) {	/* linker */
+			snprintf(msg, sizeof(msg), "NICK linker\r\n");
+			writeall(fd, msg);
+			snprintf(msg, sizeof(msg), "USER linker 0 * :linker\r\n");
+			writeall(fd, msg);
+		} else {			/* user */
+			snprintf(msg, sizeof(msg), "NICK %s\r\n", events[id].user);
+			writeall(fd, msg);
+			snprintf(msg, sizeof(msg), "USER user 0 * :user\r\n");
+			writeall(fd, msg);
 		}
-		return;
+		picoev_set_events(loop, fd, PICOEV_READ);
 	}
 
+	n = readline(fd, buffer, sizeof(buffer));
+	if (n == -1) {
+		if (errno == EAGAIN || errno == EWOULDBLOCK)
+			return;
+		printf("error: read: %d: %s\n", fd, strerror(errno));
+		clean_exit(loop, 1);
+	} else if (n == 0) {	/* reopen fifo again */
+		printf("error: closed: %d\n", fd);
+		clean_exit(loop, 1);
+	}
+
 	/* remove CRLFs */
 	for (i = 0; i < (int)strlen(buffer); i++) {
 		if (buffer[i] == '\r' || buffer[i] == '\n') {
@@ -246,11 +231,10 @@
 	strcpy(backup, buffer);
 	buf = buffer;
 
-	netid = fddata[id].netid;
 	/* set linker nick */
-	strcpy(linker_nick, "linker");
-	for (i = 0; i < fddata[id].suffix; i++)
-		strcat(linker_nick, "_");
+	strcpy(lnick, "linker");
+	for (i = 0; i < events[id].suffix; i++)
+		strcat(lnick, "_");
 
 	/* first column */
 	cmd = split(&buf, ' ');
@@ -260,7 +244,7 @@
 		goto printbuffer;
 	} else if (strcmp(cmd, "PING") == 0) {
 		snprintf(msg, sizeof(msg), "PONG %s\r\n", buf);
-		writeall(fdset[id].fd, msg);
+		writeall(fd, msg);
 		return;
 	}
 	/* strip nick from first column */
@@ -296,30 +280,30 @@
 	} else if (strcmp(cmd, "433") == 0) {	/* Nickname already in use */
 		split(&buf, ' ');
 		nick = split(&buf, ' ');
-		if (strlen(nick)+1 > MAX_NICK_LEN) {
+		if (strlen(nick)+1 > NICK_LEN) {
 			printf("error: cannot append suffix, nick '%s' is too big\n", nick);
-			if (strcmp(nick, linker_nick) == 0) {
-				net_del(networks[netid].name);
+			if (strcmp(nick, lnick) == 0) {
+				net_del(loop, networks[netid].name);
 			} else {
 				snprintf(msg, sizeof(msg), "QUIT :nick is too big\r\n");
-				user_del(nick, msg);
+				user_del(loop, nick, msg);
 			}
 		} else {
 			strcat(nick, "_");
-			fddata[id].suffix++;
+			events[id].suffix++;
 			snprintf(msg, sizeof(msg), "NICK %s\r\n", nick);
-			writeall(fdset[id].fd, msg);
+			writeall(fd, msg);
 		}
 		return;
 	} else if (strcmp(cmd, "001") == 0) {
 		snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan);
-		writeall(fdset[id].fd, msg);
+		writeall(fd, msg);
 		return;
 	} else if (strcmp(cmd, "PRIVMSG") == 0) {
 		char privmsg[BUFFER_LEN] = "";
 		int *ids;
 
-		if (fddata[id].user == NULL) {	/* if linker */
+		if (events[id].user == NULL) {	/* if linker */
 			nick_add_symb(nick, netid);
 			if ((ids = htsearch(users, nick)) == NULL)
 				return;
@@ -329,7 +313,7 @@
 			for (i = 0; i < netlen; i++) {
 				if (ids[i] > 0) {
 					snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg);
-					writeall(fdset[ids[i]].fd, msg);
+					writeall(events[ids[i]].fd, msg);
 				}
 			}
 		} else {
@@ -358,12 +342,12 @@
 			split(&buf, ':');	/* set buf to msg */
 			privmsg_update(privmsg, buf, netid);
 			snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg);
-			writeall(fdset[ids[i]].fd, msg);
+			writeall(events[ids[i]].fd, msg);
 		}
 		return;
 	}
 	/* these messages are handled by linker */
-	if (fddata[id].user != NULL) {	/* if clone */
+	if (events[id].user != NULL) {	/* if clone */
 		if ((strcmp(cmd, "353") == 0)
 		|| (strcmp(cmd, "JOIN") == 0)
 		|| (strcmp(cmd, "QUIT") == 0)
@@ -375,7 +359,7 @@
 		char *nick;
 		split(&buf, ':');
 		networks[netid].join = 1;
-		net_update(netid);
+		net_update(loop, netid);
 		while (*(nick = split(&buf, ' ')) != '\0') {
 			if (*nick == '@'
 			|| *nick == '&'
@@ -384,14 +368,14 @@
 			|| *nick == '+'
 			|| *nick == '\\')
 				nick++;
-			if (strcmp(nick, linker_nick) != 0)
-				user_add(nick, netid);
+			if (strcmp(nick, lnick) != 0)
+				user_add(loop, nick, netid);
 		}
 		return;
 	} else if (strcmp(cmd, "JOIN") == 0) {
-		if ((strcmp(nick, linker_nick) != 0)
-		&& (user_ids(nick, netid) == NULL))	/* if not clone */
-			user_add(nick, netid);
+		if ((strcmp(nick, lnick) != 0)
+		&& (clone_get_user_ids(nick, netid) == NULL))	/* if not clone */
+			user_add(loop, nick, netid);
 		return;
 	} else if ((strcmp(cmd, "QUIT") == 0)
 		|| (strcmp(cmd, "PART") == 0)) {
@@ -398,7 +382,7 @@
 		snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf);
 		nick_add_symb(nick, netid);
 		if (htsearch(users, nick) != NULL)
-			user_del(nick, msg);
+			user_del(loop, nick, msg);
 		return;
 	} else if (strcmp(cmd, "NICK") == 0) {
 		int *ids, i;
@@ -418,7 +402,7 @@
 		snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick);
 		for (i = 0; i < netlen; i++) {
 			if (ids[i] > 0)
-				writeall(fdset[ids[i]].fd, msg);
+				writeall(events[ids[i]].fd, msg);
 		}
 		return;
 	} else if (strcmp(cmd, "KICK") == 0) {
@@ -431,21 +415,21 @@
 		snprintf(msg, sizeof(msg), "QUIT : kicked by %s\r\n", nick);
 
 		/* delete whole network if it is the linker */
-		if (strcmp(user, linker_nick) == 0) {
-			net_del(networks[netid].name);
+		if (strcmp(user, lnick) == 0) {
+			net_del(loop, networks[netid].name);
 			return;
 		}
 
 		/* delete the user if the message from the same network */
-		if ((ids = user_ids(user, netid)) == NULL) {
+		if ((ids = clone_get_user_ids(user, netid)) == NULL) {
 			nick_add_symb(user, netid);
-			user_del(user, msg);
+			user_del(loop, user, msg);
 			return;
 		}
 
 		/* close the kicked fd */
-		writeall(fdset[ids[netid]].fd, msg);
-		fdset_del(id);
+		writeall(fd, msg);
+		event_del(loop, id);
 
 		/*
 		 * send notice in the channel through linker
@@ -463,18 +447,18 @@
 		snprintf(msg, sizeof(msg),
 			"PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]\r\n",
 			networks[i].chan, user, nick, networks[netid].name, chan, buf);
-		writeall(fdset[networks[i].id].fd, msg);
+		writeall(events[networks[i].id].fd, msg);
 		return;
 	}
 printbuffer:
-	printf("%d: %s\n", fdset[id].fd, backup);
+	printf("%d: %s\n", fd, backup);
 }
 
 void
-net_add(char *name, char *symb, char *host, char *port, char *chan)
+net_add(picoev_loop *loop, char *name, char *symb, char *host, char *port, char *chan)
 {
-	int	 i, fd;
-	Network *n;
+	int		i, fd;
+	struct network *n;
 
 	/* if name, symbol or configuration already exists */
 	for (i = 0; i < netlen; i++) {
@@ -497,7 +481,7 @@
 	/* resize if full */
 	if (netlen == netcap) {
 		Htiter it = {0};
-		networks = erecalloc(networks, (size_t)netcap, NET_ADDEND, sizeof(Network));
+		networks = erecalloc(networks, (size_t)netcap, NET_ADDEND, sizeof(struct network));
 		while (htiterate(users, &it))
 			it.node->val = erecalloc(it.node->val, (size_t)netcap, NET_ADDEND, sizeof(int));
 		netcap += NET_ADDEND;
@@ -506,14 +490,9 @@
 	/* connect */
 	if ((fd = dial(host, port)) == -1)
 		return;
-	/* send NICK and USER commands */
-	snprintf(msg, sizeof(msg), "NICK linker\r\n");
-	writeall(fd, msg);
-	snprintf(msg, sizeof(msg), "USER linker 0 * :linker\r\n");
-	writeall(fd, msg);
 	/* add a network */
 	n = &networks[netlen];
-	n->id = fdset_add(fd, netlen, 0, NULL);
+	n->id = event_add(loop, fd, netlen, NULL);
 	n->join = 0;
 	n->name = strdup(name);
 	n->symb = strdup(symb);
@@ -525,7 +504,7 @@
 }
 
 void
-net_del(char *name)
+net_del(picoev_loop *loop, char *name)
 {
 	int	i, netid, *ids;
 
@@ -553,14 +532,14 @@
 		ids = (int *)it.node->val;
 		/* delete all the users of deleting network */
 		if (ids[netid] == -1) {
-			user_del(it.node->key, msg);
+			user_del(loop, it.node->key, msg);
 			/* this node is deleted */
 			it = lastit;
 		/* delete the clones */
 		} else {
 			if (ids[netid] > 0) {
-				writeall(fdset[ids[netid]].fd, msg);
-				fdset_del(ids[netid]);
+				writeall(events[ids[netid]].fd, msg);
+				event_del(loop, ids[netid]);
 			}
 			/* swap last with current one */
 			ids[netid] = ids[netlen-1];
@@ -568,16 +547,16 @@
 		lastit = it;
 	}
 
-	/* set netid of fds with last netid to current netid. */
-	for (i = 0; i < fdslen; i++) {
-		if (fddata[i].netid == netlen-1)
-			fddata[i].netid = netid;
+	/* set last netid of events to current netid. */
+	for (i = 0; i < evlen; i++) {
+		if (events[i].netid == netlen-1)
+			events[i].netid = netid;
 	}
 
-	writeall(fdset[networks[netid].id].fd, msg);
-	fdset_del(networks[netid].id);
+	writeall(events[networks[netid].id].fd, msg);
+	event_del(loop, networks[netid].id);
 	net_del_raw(netid);
-	printf("%d: network '%s' deleted\n", fdset[networks[netid].id].fd, name);
+	printf("%d: network '%s' deleted\n", events[networks[netid].id].fd, name);
 	/* swap the network with the last */
 	networks[netid] = networks[netlen-1];
 	netlen--;
@@ -586,7 +565,7 @@
 void
 net_del_raw(int netid)
 {
-	Network *n = &networks[netid];
+	struct network *n = &networks[netid];
 	free(n->name);
 	free(n->symb);
 	free(n->host);
@@ -595,15 +574,15 @@
 }
 
 void
-net_update(int netid)
+net_update(picoev_loop *loop, int netid)
 {
 	Htiter it = {0};
 	while (htiterate(users, &it))
-		((int *)it.node->val)[netid] = clone_add(it.node->key, netid);
+		((int *)it.node->val)[netid] = clone_add(loop, it.node->key, netid);
 }
 
 void
-user_add(char *unick, int netid)
+user_add(picoev_loop *loop, char *unick, int netid)
 {
 	int	i, *ids;
 	size_t	len;
@@ -612,12 +591,12 @@
 	len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1;
 
 	/* too long nick */
-	if (len-1 > MAX_NICK_LEN) {
+	if (len - 1 > NICK_LEN) {
 		printf("error: user nick '%s' is too big\n", unick);
 		return;
 	}
 
-	/* resize hash table if store is low */
+	/* resize hash table if storage is low */
 	if ((users->cap - users->len) < USER_ADDEND)
 		htresize(users, users->cap + USER_ADDEND);
 
@@ -635,7 +614,7 @@
 		if (i == netid) {
 			ids[i] = -1;
 		} else {
-			ids[i] = clone_add(nick, i);
+			ids[i] = clone_add(loop, nick, i);
 		}
 	}
 	/* insert it to the users hash table */
@@ -642,19 +621,20 @@
 	if (htinsert(users, nick, ids) == -1) {
 		/* this shouldn't happen as it was already checked */
 		printf("error: user '%s' already exists\n", nick);
-		terminate(1);
+		clean_exit(loop, 1);
 	}
 }
 
 void
-user_del(char *nick, char *msg)
+user_del(picoev_loop *loop, char *nick, char *msg)
 {
 	int i;
-	int *ids = euserids(nick);
+	int *ids = user_get_ids(nick);
+
 	for (i = 0; i < netlen; i++) {
 		if (ids[i] > 0) {
-			writeall(fdset[ids[i]].fd, msg);
-			fdset_del(ids[i]);
+			writeall(events[ids[i]].fd, msg);
+			event_del(loop, ids[i]);
 		}
 	}
 	htremove(users, nick);
@@ -661,8 +641,19 @@
 }
 
 int *
-user_ids(char *nick, int netid)
+user_get_ids(char *user)
 {
+	int *ids;
+	if ((ids = htsearch(users, user)) == NULL) {
+		printf("error: cannot find user '%s'\n", user);
+		exit(1);
+	}
+	return ids;
+}
+
+int *
+clone_get_user_ids(char *nick, int netid)
+{
 	unsigned int suffix;
 	int *ids = NULL;
 
@@ -674,7 +665,7 @@
 
 	ids = htsearch(users, nick);
 	/* if match but suffix doesn't match */
-	if ((ids != NULL) && (fddata[ids[netid]].suffix != (int)suffix))
+	if ((ids != NULL) && (events[ids[netid]].suffix != (int)suffix))
 		ids = NULL;
 
 	/* add suffix back */
@@ -684,68 +675,64 @@
 	return ids;
 }
 
-/* get user ids */
-int *
-euserids(char *user)
-{
-	int *ids;
-	if ((ids = htsearch(users, user)) == NULL) {
-		printf("error: cannot find user '%s'\n", user);
-		terminate(1);
-	}
-	return ids;
-}
-
 int
-clone_add(char *nick, int netid)
+clone_add(picoev_loop *loop, char *nick, int netid)
 {
-	int	fd;
-	Network	*n = &networks[netid];
+	int		fd;
+	struct network	*n = &networks[netid];
+
 	if ((fd = dial(n->host, n->port)) == -1)
 		return -1;
-	/* send NICK and USER commands */
-	snprintf(msg, sizeof(msg), "NICK %s\r\n", nick);
-	writeall(fd, msg);
-	snprintf(msg, sizeof(msg), "USER user 0 * :user\r\n");
-	writeall(fd, msg);
-	return fdset_add(fd, netid, 0, nick);
+	return event_add(loop, fd, netid, nick);
 }
 
 int
-fdset_add(int fd, int netid, int suffix, char *user)
+event_add(picoev_loop *loop, int fd, int netid, char *user)
 {
-	if (fdslen == fdscap) {
-		fdset = erecalloc(fdset, (size_t)fdscap, POLLFD_ADDEND, sizeof(struct pollfd));
-		fddata = erecalloc(fddata, (size_t)fdscap, POLLFD_ADDEND, sizeof(struct FdData));
-		fdscap += POLLFD_ADDEND;
+	int i = evlen;
+
+	if (evlen == evcap) {
+		events = erecalloc(events, (size_t)evcap, EVENT_ADDEND, sizeof(struct event));
+		evcap += EVENT_ADDEND;
 	}
-	fdset[fdslen].fd = fd;
-	fdset[fdslen].events = POLLIN;
-	fddata[fdslen].netid = netid;
-	fddata[fdslen].suffix = suffix;
-	fddata[fdslen].user = user;
-	fddata[fdslen].time = time(NULL);
-	return fdslen++;
+
+	events[i].fd = fd;
+	events[i].netid = netid;
+	events[i].user = user;
+	events[i].suffix = 0;
+	picoev_add(loop, fd, PICOEV_WRITE, 0, server_event_cb, &events[i]);
+
+	return evlen++;
 }
 
 void
-fdset_del(int id)
+event_del(picoev_loop *loop, int id)
 {
-	/* set id of last fd to current id */
-	if (fddata[fdslen-1].user != NULL)
-		euserids(fddata[fdslen-1].user)[fddata[fdslen-1].netid] = id;
-	else if ((fddata[fdslen-1].user == NULL) && (fddata[fdslen-1].netid != -1))
-		networks[fddata[fdslen-1].netid].id = id;
-	/* set id of removing fd to -2 */
-	if (fddata[id].user != NULL)
-		euserids(fddata[id].user)[fddata[id].netid] = -2;
-	else if ((fddata[id].user == NULL) && (fddata[id].netid != -1))
-		networks[fddata[id].netid].id = -2;
+	int *ids;
+	int  l = evlen - 1;			/* last id */
 
-	close(fdset[id].fd);
-	fdset[id] = fdset[fdslen-1];
-	fddata[id] = fddata[fdslen-1];
-	fdslen--;
+	/* swap id */
+	if (events[l].user == NULL) {			/* if linker */
+		networks[events[l].netid].id = id;
+	} else {					/* if user */
+		ids = user_get_ids(events[l].user);
+		ids[events[l].netid] = id;
+	}
+
+	/* disable id */
+	if (events[id].user == NULL) {			/* if linker */
+		networks[events[id].netid].id = -2;
+	} else {					/* if user */
+		ids = user_get_ids(events[id].user);
+		ids[events[id].netid] = -2;
+	}
+
+	picoev_del(loop, events[id].fd);
+	close(events[id].fd);
+
+	events[id] = events[l];
+	events[id] = events[l];
+	evlen--;
 }
 
 void
@@ -777,7 +764,7 @@
 		}
 
 		/* check if the word is nick */
-		if (user_ids(src, netid) != NULL)
+		if (clone_get_user_ids(src, netid) != NULL)
 			*strrchr(src, '[') = '\0';
 
 		strcat(dst, src);
@@ -788,15 +775,17 @@
 }
 
 void
-terminate(int status)
+clean_exit(picoev_loop *loop, int status)
 {
 	int i;
 	snprintf(msg, sizeof(msg), "QUIT :linker shutting down\r\n");
-	for (i = 0; i < fdslen; i++) {
-		if (fddata[i].netid != -1)
-			writeall(fdset[i].fd, msg);
-		fdset_del(i);
+	for (i = 0; i < evlen; i++) {
+		writeall(events[i].fd, msg);
+		event_del(loop, i);
 	}
+	picoev_destroy_loop(loop);
+	picoev_deinit();
+
 	/* delete all the users */
 	htdestroy(users);
 
@@ -805,8 +794,7 @@
 		net_del_raw(i);
 
 	free(networks);
-	free(fdset);
-	free(fddata);
+	free(events);
 
 	if (status == 0) {
 		printf("exit successfully\n");
@@ -829,9 +817,9 @@
 
 	print_border();
 	/* print networks */
-	printf("Networks\t\t");
+	printf("struct networks\t\t");
 	for (i = 0; i < netlen; i++)
-		printf("%s(%d)\t", networks[i].symb, fdset[networks[i].id].fd);
+		printf("%s(%d)\t", networks[i].symb, events[networks[i].id].fd);
 	printf("\n");
 
 	while (htiterate(users, &it)) {
@@ -846,8 +834,8 @@
 		for (i = 0; i < netlen; i++) {
 			printf("%d", ids[i]);
 			/* print suffix */
-			if ((ids[i] > 0) && (fddata[ids[i]].suffix > 0))
-				printf("(%d)", fddata[ids[i]].suffix);
+			if ((ids[i] > 0) && (events[ids[i]].suffix > 0))
+				printf("(%d)", events[ids[i]].suffix);
 			printf("\t");
 		}
 		printf("\n");