wm: ticl

Download patch

ref: 8df402e53d02ca2c0b976715ce57c28750dd99d9
parent: 5ff9649e6c51bae0dd09f218e4943fa5f4d458be
author: libredev <libredev@ircforever.org>
date: Wed Nov 16 13:14:04 EST 2022

code refactor

--- a/Makefile
+++ b/Makefile
@@ -12,15 +12,11 @@
 
 VERSION != date '+%Y-%m-%d'
 PROGRAM = ticl
-SOURCES = main.c htable.c utils.c
-HEADERS = htable.h utils.h
+SOURCES = main.c htable.c
+HEADERS = htable.h util.c
 OBJECTS = $(SOURCES:.c=.o)
 
-all: $(PROGRAM)
-
-main.o: htable.o utils.o
-htable.o: htable.c htable.h
-utils.o: utils.c utils.h
+all: clean $(PROGRAM)
 
 $(PROGRAM): $(OBJECTS)
 	$(CC) -o $@ $(OBJECTS) $(LDFLAGS)
--- a/htable.c
+++ b/htable.c
@@ -3,11 +3,12 @@
  * See COPYING file for more information.
  */
 
+#include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "htable.h"
-#include "utils.h"
 
 static unsigned int
 hash(Htable *ht, void *key)
@@ -28,11 +29,20 @@
 	unsigned int i;
 	Htable *ht;
 
-	if (keylenfn == NULL)	fatal("keylenfn cannot be NULL");
-	if (keycmpfn == NULL)	fatal("keycmpfn cannot be NULL");
+	if (keylenfn == NULL || keycmpfn == NULL || freekeyfn == NULL || freevalfn == NULL) {
+		printf("error: callback function(s) cannot be NULL\n");
+		return NULL;
+	}
 
-	ht = ecalloc(1, sizeof(Htable));
-	ht->nodes	= ecalloc(cap, sizeof(Htnode));
+	/* create a hash table */
+	if ((ht = calloc(1, sizeof(Htable))) == NULL) {
+		printf("error: calloc: %s\n", strerror(errno));
+		return NULL;
+	}
+	if ((ht->nodes = calloc(cap, sizeof(Htnode))) == NULL) {
+		printf("error: calloc: %s\n", strerror(errno));
+		return NULL;
+	}
 	ht->cap		= cap;
 	ht->len		= 0;
 	ht->keylenfn	= keylenfn;
@@ -103,13 +113,16 @@
 			ht->freevalfn(n->val);
 			n->key = key;
 			n->val = val;
-			return -1;
+			return 0;
 		}
 		pn = n;
 	}
 
 	/* create a new node */
-	n = ecalloc(1, sizeof(Htnode));
+	if ((n = calloc(1, sizeof(Htnode))) == NULL) {
+		printf("error: calloc: %s\n", strerror(errno));
+		return -1;
+	}
 	n->key = key;
 	n->val = val;
 	n->next = NULL;
--- a/main.c
+++ b/main.c
@@ -12,14 +12,15 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "utils.h"
 #include "htable.h"
+#include "util.c"
 
 #define BUFFER_LEN	1024
 #define MAX_FD		1024
 
+#define MAX_NICK_LEN	16
 #define NET_ADDEND	10
-#define USER_ADDEND	100
+#define USER_ADDEND	5
 
 #define FD_TYPE_LINKER	1
 #define FD_TYPE_CLONE	2
@@ -33,6 +34,8 @@
 	char	*chan;	/* channel	*/
 } Network;
 
+static int	 isrunning = 1;
+
 static fd_set	 fdset;			/* fd_set (select)	*/
 static int	 nfds;			/* number of fds	*/
 
@@ -42,389 +45,181 @@
 
 static Network	*networks;		/* linked list of networks */
 static int	 netlen;		/* current network length  */
-static int	 netcap = NET_ADDEND;	/* total memory allocated  */
+static int	 netcap;		/* total memory allocated  */
+static Htable	*users;	
 
-/*
- * hash table of users
- * key   -> <nickname> + '[' + <network_symbol> + ']'
- * value -> array of linked clones fds indexed
- *	    corresponding to its connected network
- */
-static Htable	 *users;	
+/* functions prototype */
+void	 handle_fifo_input(int);
+void	 handle_server_output(int);
+void	 net_add(char *, char *, char *, char *, char *);
+void	 net_del(char *);
+void	 net_del_raw(int);
+void	 net_resize(void);
+void	 net_update(void);
+void	 user_add(char *, int);
+void	 user_del(char *, char *);
+int	*user_fds(char *, int);
+int	 clone_add(int, char *);
+int	 fd_add(char *, char *);
+void	 fd_del(int, char *);
+void   	 nick_add_symb(char *, int);
+void   	 privmsg_update(char *, char *, int);
+void   	 terminate(int);
+void   	 print_table(void);
+void   	 print_htable(void);
+void   	 print_users(void);
+void	 print_border(void);
 
-static int	 isrunning = 1;
-
-/* prototype */
-void	 print_table(void);
-void	 print_users(void);
-int	 fdadd(char *host, char *port);
-void	 fddel(int fd, char *msg);
-void	 netadd(char *name, char *symb, char *host, char *port, char *chan);
-void	 __netdel(int index);
-void	 netdel(char *name);
-int	 cloneadd(int netindex, char *nick);
-void	 clonesupdate(void);
-void	 useradd(char *unick, int nfd);
-void	 userdel(char *nick, char *msg);
-void	 append_netsymb(char *nick, int fd);
-int	*getuserfds(char *nick, int nfd);
-void	 privmsg_update(char *dst, char *src, int fd);
-void	 handle_server(int fd);
-void	 handle_fifo(int fd);
-
-void
-print_table(void)
-{
-	int i, *fds;
-	Htiter	it = {0};
-
-	if (netlen == 0) return;
-
-	/* print networks */
-	printf("Networks\t");
-	for (i = 0; i < netlen; i++)
-		printf("%s[%s](%d)\t", networks[i].name, networks[i].symb, networks[i].fd);
-	printf("\n");
-
-	while (htiterate(users, &it)) {
-		fds = (int *)it.node->val;
-		/* print user nick */
-		printf("%s\t", (char *)it.node->key);
-		/* print clones fd separated by tabs */
-		for (i = 0; i < netlen; i++) {
-			printf("%d", fds[i]);
-			if ((fds[i] > 0) && (fdsuf[fds[i]] > 0))
-				printf("(%d)", fdsuf[fds[i]]);
-			printf("\t");
-		}
-		printf("\n");
-	}
-}
-
-
-void
-print_users(void)
-{
-	Htiter	it = {0};
-	int	index = -1;
-	while (htiterate(users, &it)) {
-		if (index != (int)it.index) {
-			printf("\n%d", it.index);
-			index = (int)it.index;
-		}
-		printf(" -> %s", (char *)it.node->key);
-	}
-	printf("\n");
-}
-
 int
-fdadd(char *host, char *port)
+main(int argc, char *argv[])
 {
-	int fd;
-	if ((fd = dial(host, port)) == -1)
-		return -1;
+	int		fifo_fd, r, i;
+	fd_set		rdset;
+	struct stat	fifo_st;
 
-	FD_SET(fd, &fdset);
-	if (fd+1 > nfds)
-		nfds = fd+1;
+	/* set stdout to unbufferd */
+	setvbuf(stdout, NULL, _IONBF, 0);
 
-	return fd;
-}
-
-void
-fddel(int fd, char *msg)
-{
-	ircsend(fd, "QUIT :%s", msg);
-	close(fd);
-	FD_CLR(fd, &fdset);
-	fdnet[fd] = -1;
-}
-
-void
-netadd(char *name, char *symb, char *host, char *port, char *chan)
-{
-	int i, fd;
-	Network *n;
-	Htiter	it = {0};
-
-	/* resize */
-	if (netlen < netcap) {
-		netcap += NET_ADDEND;
-		networks = realloc(networks, (size_t)netcap * sizeof(Network));
-
-		while (htiterate(users, &it))
-			it.node->val = realloc(it.node->val, (size_t)netcap * sizeof(int));
+	/* check arguments */
+	if (argc != 2) {
+		printf("usage: %s <fifo>\n", argv[0]);
+		return 1;
 	}
 
-	/* if name or symbol or host:port is already taken */
-	for (i = 0; i < netlen; i++) {
-		if (strcmp(networks[i].name, name) == 0) {
-			printf("error: network name '%s' already taken\n", name);
-			return;
-		}
-		if (strcmp(networks[i].symb, symb) == 0) {
-			printf("error: network symbol '%s' already taken\n", symb);
-			return;
-		}
-		/* if ((strcmp(networks[i].host, host) == 0)
-		&& (strcmp(networks[i].port, port) == 0)) {
-			printf("error: network host '%s' and port '%s' already taken by network '%s'\n", host, port, networks[i].name);
-			return;
-		}*/
-	}
+	/* init networks and users */
+	netcap = NET_ADDEND;
+	networks = ecalloc((size_t)netcap, sizeof(Network));
 
-	printf("info: adding network '%s'\n", name);
-	if ((fd = fdadd(host, port)) == -1)
-		return;
-	/* send NICK and USER commands */
-	ircsend(fd, "NICK linker");
-	ircsend(fd, "USER linker localhost %s :%s", host, name);
-	/* add a network */
-	fdnet[fd] = netlen;
-	fdtype[fd] = FD_TYPE_LINKER;
+	/*
+	 * hash table of users
+	 * key   -> <nickname> + '[' + <network_symbol> + ']'
+	 * value -> array of linked clones fds indexed
+	 *	    corresponding to its connected network
+	 */
+	users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND);
 
-	n = &networks[netlen];
-	n->fd = fd;
-	n->name = strdup(name);
-	n->symb = strdup(symb);
-	n->host = strdup(host);
-	n->port = strdup(port);
-	n->chan = strdup(chan);
-	netlen++;
-}
+	/* init fdnet array to -1 */
+	for (i = 0; i < MAX_FD; i++)
+		fdnet[i] = -1;
 
-void
-__netdel(int index)
-{
-	Network *n = &networks[index];
-	free(n->name);
-	free(n->symb);
-	free(n->host);
-	free(n->port);
-	free(n->chan);
-}
-
-void
-netdel(char *name)
-{
-	int i, netindex = -1, *fds;
-	char quit_msg[BUFFER_LEN];
-
-	Htiter	it	= {0};	/* current iterator */
-	Htiter	lastit	= {0};	/* last iterator */
-
-	/* check if the netname exists */
-	for (i = 0; i < netlen; i++) {
-		if (strcmp(name, networks[i].name) == 0) {
-			netindex = i;
-			break;
+	/* crate or/and open fifo */
+	if (stat(argv[1], &fifo_st) == 0) {
+		if (!(fifo_st.st_mode & S_IFIFO)) {
+			printf("error: '%s' is not a fifo file\n", argv[1]);
+			return 1;
 		}
+	} else if (mkfifo(argv[1], S_IRWXU) != 0) {
+		printf("error: failed to create fifo file '%s'\n", argv[1]);
+		return 1;
 	}
-	if (netindex == -1) {
-		printf("error: network name '%s' doesn't exist\n", name);
-		return;
+	fifo_fd = open(argv[1], O_RDWR);
+	if (fifo_fd < 0) {
+		printf("error: cannot open() '%s'\n", argv[1]);
+		return 1;
 	}
 
-	printf("info: deleting network '%s'\n", name);
+	/* initialize fdset */
+	FD_ZERO(&fdset);
+	FD_SET(fifo_fd, &fdset);
+	nfds = fifo_fd + 1;
 
-	/* set the quit msg */
-	snprintf(quit_msg, BUFFER_LEN, "unlinking network %s", name);
-
-	/* set all fds with last network index to the current index. */
-	for (i = 0; i < nfds; i++) {
-		if (fdnet[i] == netlen-1)
-			fdnet[i] = netindex;
-	}
-
-	while (htiterate(users, &it)) {
-		fds = (int *)it.node->val;
-		/* delete the user */
-		if (fds[netindex] == -1) {
-			userdel(it.node->key, quit_msg);
-			/* this node is deleted and has no next */
-			it = lastit;
-		/* delete the clone */
-		} else {
-			if (fds[netindex] > 0)
-				fddel(fds[netindex], quit_msg);
-
-			/* swap last index with the netindex */
-			fds[netindex] = fds[netlen-1];
-			fds[netlen-1] = 0;
+	/* select loop */
+	while (isrunning) {
+		rdset = fdset;
+		r = select(nfds, &rdset, 0, 0, NULL);
+		if (r < 0) {
+			if (errno == EINTR)
+				continue;
+			printf("error: select: %s\n", strerror(errno));
+			terminate(1);
+		} else if (r == 0) {
+			printf("error: select timeout\n");
+			terminate(1);
 		}
-		lastit = it;
-	}
 
-	fddel(networks[netindex].fd, quit_msg);
-	 __netdel(netindex);
-
-	/* swap the network with the last */
-	networks[netindex] = networks[netlen-1];
-	netlen--;
-}
-
-int
-cloneadd(int netindex, char *nick)
-{
-	int fd;
-	Network *n = &networks[netindex];
-
-	printf("info: adding clone '%s' on network '%s'\n", nick, n->name);
-	if ((fd = fdadd(n->host, n->port)) == -1)
-		return -1;
-	/* send NICK and USER commands */
-	ircsend(fd, "NICK %s", nick);
-	ircsend(fd, "USER username localhost %s :real name", n->host);
-	/* add a user */
-	fdnet[fd] = netindex;
-	fdtype[fd] = FD_TYPE_CLONE;
-	fdsuf[fd] = 0;
-	return fd;
-}
-
-/* update clones in the last network added */
-void
-clonesupdate(void)
-{
-	int	*fds;
-	Htiter	 it = {0};
-
-	while (htiterate(users, &it)) {
-		fds = (int *)it.node->val;
-		/* netlen-1 can be occupied if networks are added quickly
-		 * and has not enough time to connect to it's server */
-		if (fds[netlen-1] == 0)
-			fds[netlen-1] = cloneadd(netlen-1, it.node->key);
-	}
-}
-
-void
-useradd(char *unick, int nfd)
-{
-	int i, *fds;
-	Network *n;
-	size_t l;
-	char *nick;
-
-	n = &networks[fdnet[nfd]];
-
-	/* too long name */
-	l = strlen(unick) + strlen(n->symb) + 2 + 1;
-	if (l > 16)
-		return;
-
-	nick = ecalloc(l, sizeof(char));
-	fds = ecalloc((size_t)netcap, sizeof(int));
-	sprintf(nick, "%s[%s]", unick, n->symb);
-
-	/* resize */
-	if ((users->cap - users->len) < USER_ADDEND)
-		htresize(users, users->cap + USER_ADDEND);
-
-	for (i = 0; i < netlen; i++) {
-		/* set -1 on user network */
-		if (i == fdnet[nfd]) {
-			fds[i] = -1;
-		} else {
-			fds[i] = cloneadd(i, nick);
+		for (i = 0; i < nfds; i++) {
+			if (!FD_ISSET(i, &rdset))
+				continue;
+			if (i == fifo_fd)
+				handle_fifo_input(i);
+			else
+				handle_server_output(i);
+			break;	/* one fd at a time because of FD_CLR */
 		}
 	}
-
-	if (htinsert(users, nick, fds) == -1)
-		fatal("error: user '%s' already exist", nick);
+	terminate(0);
+	return 0;
 }
 
 void
-userdel(char *nick, char *msg)
+handle_fifo_input(int fd)
 {
-	int *fds, i;
-	if ((fds = htsearch(users, nick)) == NULL)
-		return;
+	char buffer[BUFFER_LEN];
+	char *buf;
+	char *cmd;
 
-	for (i = 0; i < netlen; i++) {
-		if (fds[i] > 0)
-			fddel(fds[i], msg);
+	if (readline(fd, buffer, sizeof(buffer)) < 1) {
+		printf("error: %d: failed to read from fifo: %s\n", fd, strerror(errno));
+		terminate(1);
 	}
-	htremove(users, nick);
-}
+	/* printf("fifo: %s\n", buffer); */
 
-void
-append_netsymb(char *nick, int fd)
-{
-	strcat(nick, "[");
-	strcat(nick, networks[fdnet[fd]].symb);
-	strcat(nick, "]");
-}
-
-int *
-getuserfds(char *nick, int nfd)
-{
-	unsigned int l;
-	int *fds = NULL;
-
-	for (l = 0; nick[strlen(nick)-l-1] == '_'; l++);
-	if (l > 0)
-		nick[strlen(nick)-l] = '\0';
-
-	fds = htsearch(users, nick);
-	/* if match but suffix doesn't match */
-	if ((fds != NULL) && (fdsuf[fds[fdnet[nfd]]] != (int)l))
-		fds = NULL;
-
-	/* add the removed delimiter */
-	if (l > 0)
-		nick[strlen(nick)] = '_';
-
-	return fds;
-}
-
-/* trim all the nicknames to original nick
- * this src will be destructed */
-void
-privmsg_update(char *dst, char *src, int fd)
-{
-	char d;		/* delimiter */
-	char *n;
-
-	while (src != NULL) {
-		n = strpbrk(src, " :;,<>@&~%+\\");
-		if (n == NULL) {
-			d = '\0';
-		} else {
-			d = *n;
-			*n = '\0';
-			n++;
-		}
-
-		/* check if the word is nick */
-		if (getuserfds(src, fd) != NULL)
-			*strrchr(src, '[') = '\0';
-
-		strcat(dst, src);
-		strncat(dst, &d, 1);
-
-		src = n;
+	buf = buffer;
+	cmd = split(&buf, ' ');
+	if (strcmp(cmd, "netadd") == 0) {
+		char *name = split(&buf, ' ');
+		char *symb = split(&buf, ' ');
+		char *host = split(&buf, ' ');
+		char *port = split(&buf, ' ');
+		char *chan = buf;
+		if (!*name || !*symb || !*host || !*port || !*chan)
+			printf("usage: netadd <name> <symbol> <hostname> <port> <channel>\n");
+		else
+			net_add(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);
+	} else if (strcmp(cmd, "print") == 0) {
+		print_table();
+	} else if (strcmp(cmd, "htable") == 0) {
+		print_htable();
+	} else if (strcmp(cmd, "users") == 0) {
+		print_users();
+	} else if (strcmp(cmd, "exit") == 0) {
+		isrunning = 0;
+	} else {
+		printf("error: %s is not a command\n", cmd);
 	}
 }
 
 void
-handle_server(int fd)
+handle_server_output(int fd)
 {
 	char backup [BUFFER_LEN];
 	char buffer [BUFFER_LEN];
+	char linker_nick [MAX_NICK_LEN];
 	char *buf;
 	char *cmd;
 	char *nick;
 	int i;
 
-	char linker[BUFFER_LEN] = "linker";
+	if (readline(fd, buffer, sizeof(buffer)) < 1) {
+		printf("error: %d: remote host closed connection: %s\n", fd, strerror(errno));
+		terminate(1);
+	}
 
-	if (dreadline(fd, buffer, sizeof(buffer)) < 1)
-		fatal("error: %d: remote host closed connection: %s", fd, strerror(errno));
-
+	/* clone the buffer */
 	strcpy(backup, buffer);
 	buf = buffer;
 
+	/* set linker nick */
+	strcpy(linker_nick, "linker");
+	for (i = 0; i < fdsuf[fd]; i++)
+		strcat(linker_nick, "_");
+
 	/* remove CRLFs */
 	for (i = 0; i < (int)strlen(buf); i++) {
 		if (buf[i] == '\r' || buf[i] == '\n') {
@@ -440,7 +235,7 @@
 	} else if (strcmp(cmd, "ERROR") == 0) {
 		goto printbuffer;
 	} else if (strcmp(cmd, "PING") == 0) {
-		ircsend(fd, "PONG %s", buf);
+		fdprintf(fd, "PONG %s\r\n", buf);
 		return;
 	}
 	/* strip nick from first column */
@@ -450,20 +245,7 @@
 
 	/* second column */
 	cmd = split(&buf, ' ');
-	/* these messages are handled by linker */
-	if (fdtype[fd] == FD_TYPE_CLONE) {
-		if ((strcmp(cmd, "353") == 0)
-		|| (strcmp(cmd, "JOIN") == 0)
-		|| (strcmp(cmd, "QUIT") == 0)
-		|| (strcmp(cmd, "PART") == 0)
-		|| (strcmp(cmd, "KICK") == 0)
-		|| (strcmp(cmd, "NICK") == 0))
-			return;
-	}
 
-	/* set linker nick */
-	for (i = 0; i < fdsuf[fd]; i++)
-		strcat(linker, "_");
 	/* ignore all the info messages */
 	if ((strcmp(cmd, "002") == 0)
 	|| (strcmp(cmd, "003") == 0)
@@ -472,6 +254,7 @@
 	|| (strcmp(cmd, "003") == 0)
 	|| (strcmp(cmd, "251") == 0)
 	|| (strcmp(cmd, "252") == 0)
+	|| (strcmp(cmd, "253") == 0)	/* unknown connection(s) */
 	|| (strcmp(cmd, "254") == 0)
 	|| (strcmp(cmd, "255") == 0)
 	|| (strcmp(cmd, "265") == 0)
@@ -490,17 +273,79 @@
 		nick = split(&buf, ' ');
 		strcat(nick, "_");
 		fdsuf[fd]++;
-		ircsend(fd, "NICK %s", nick);
+		if (strlen(nick) > MAX_NICK_LEN) {
+			/* remove suffix from the nick */
+			nick[strlen(nick) - (size_t)fdsuf[fd]] = '\0';
+			printf("error: cannot append suffix, nick '%s' is too big\n", nick);
+			if (strcmp(nick, "linker") == 0) {
+				net_del(networks[fdnet[fd]].name);
+			} else {
+				user_del(nick, "nick is too big");
+			}
+		} else {
+			fdprintf(fd, "NICK %s\r\n", nick);
+		}
 		return;
 	} else if (strcmp(cmd, "001") == 0) {
-		ircsend(fd, "JOIN %s", networks[fdnet[fd]].chan);
+		fdprintf(fd, "JOIN %s\r\n", networks[fdnet[fd]].chan);
 		return;
+	} else if (strcmp(cmd, "PRIVMSG") == 0) {
+		char msg[BUFFER_LEN] = "";
+		int *fds;
 
-	/* ========================= only linker ========================= */
+		if (fdtype[fd] == FD_TYPE_LINKER) {
+			nick_add_symb(nick, fd);
+			if ((fds = htsearch(users, nick)) == NULL)
+				return;
+
+			split(&buf, ':');	/* set buf to msg */
+			privmsg_update(msg, buf, fd);
+			for (i = 0; i < netlen; i++) {
+				if (fds[i] > 0)
+					fdprintf(fds[i], "PRIVMSG %s :%s\r\n", networks[i].chan, msg);
+			}
+		} else {
+			char *netsymb;
+			char *user = split(&buf, ' ');
+
+			/* ignore messages from channel (it is handled by linker) */
+			if ((user[0] == '#') || (user[0] == '&'))
+				return;
+
+			nick_add_symb(nick, fd);
+			if ((fds = htsearch(users, nick)) == NULL)
+				return;
+
+			/* split user nick and network symbol */
+			*strrchr(user, ']') = '\0';
+			netsymb = strrchr(user, '[');
+			*netsymb++ = '\0';
+
+			/* get the network index */
+			for (i = 0; i < netlen; i++) {
+				if (strcmp(netsymb, networks[i].symb) == 0)
+					break;
+			}
+
+			split(&buf, ':');	/* set buf to msg */
+			privmsg_update(msg, buf, fd);
+			fdprintf(fds[i], "PRIVMSG %s :%s\r\n", user, msg);
+		}
+		return;
+	}
+	/* these messages are handled by linker */
+	if (fdtype[fd] == FD_TYPE_CLONE) {
+		if ((strcmp(cmd, "353") == 0)
+		|| (strcmp(cmd, "JOIN") == 0)
+		|| (strcmp(cmd, "QUIT") == 0)
+		|| (strcmp(cmd, "PART") == 0)
+		|| (strcmp(cmd, "KICK") == 0)
+		|| (strcmp(cmd, "NICK") == 0))
+			return;
 	} else if (strcmp(cmd, "353") == 0) {
 		char *nick;
 		split(&buf, ':');
-		clonesupdate();
+		net_update();
 		while (*(nick = split(&buf, ' ')) != '\0') {
 			if (*nick == '@'
 			|| *nick == '&'
@@ -509,25 +354,25 @@
 			|| *nick == '+'
 			|| *nick == '\\')
 				nick++;
-			if (strcmp(nick, linker) != 0)
-				useradd(nick, fd);
+			if (strcmp(nick, linker_nick) != 0)
+				user_add(nick, fd);
 		}
 		return;
 	} else if (strcmp(cmd, "JOIN") == 0) {
-		if ((strcmp(nick, linker) != 0)
-		&& (getuserfds(nick, fd) == NULL))	/* if not clone */
-			useradd(nick, fd);
+		if ((strcmp(nick, linker_nick) != 0)
+		&& (user_fds(nick, fd) == NULL))	/* if not clone */
+			user_add(nick, fd);
 		return;
 	} else if ((strcmp(cmd, "QUIT") == 0)
 		|| (strcmp(cmd, "PART") == 0)) {
-		append_netsymb(nick, fd);
-		userdel(nick, buf);
+		nick_add_symb(nick, fd);
+		user_del(nick, buf);
 		return;
 	} else if (strcmp(cmd, "NICK") == 0) {
 		int *fds, i;
 		char *newnick;
 
-		append_netsymb(nick, fd);
+		nick_add_symb(nick, fd);
 		if ((fds = htsearch(users, nick)) == NULL)
 			return;
 
@@ -540,7 +385,7 @@
 
 		for (i = 0; i < netlen; i++) {
 			if (fds[i] > 0)
-				ircsend(fds[i], "NICK %s", newnick);
+				fdprintf(fds[i], "NICK %s\r\n", newnick);
 		}
 		return;
 	} else if (strcmp(cmd, "KICK") == 0) {
@@ -553,15 +398,15 @@
 		/* set the quit msg */
 		snprintf(quit_msg, BUFFER_LEN, "kicked by %s", nick);
 
-		if (strcmp(user, linker) == 0) {
-			netdel(networks[fdnet[fd]].name);
+		if (strcmp(user, linker_nick) == 0) {
+			net_del(networks[fdnet[fd]].name);
 			return;
 		}
 
 		/* delete the user if the message from the same network */
-		if ((fds = getuserfds(user, fd)) == NULL) {
-			append_netsymb(user, fd);
-			userdel(user, quit_msg);
+		if ((fds = user_fds(user, fd)) == NULL) {
+			nick_add_symb(user, fd);
+			user_del(user, quit_msg);
 			return;
 		}
 
@@ -577,173 +422,344 @@
 		/* set buf to msg */
 		split(&buf, ':');	
 		/* send notice in the channel through linker */
-		ircsend(networks[i].fd, "PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]",
+		fdprintf(networks[i].fd, "PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]\r\n",
 				networks[i].chan, user, nick, networks[fdnet[fd]].name, chan, buf);
 		
 		/* close the kicked fd */
-		fddel(fds[fdnet[fd]], quit_msg);
+		fd_del(fds[fdnet[fd]], quit_msg);
 		fds[fdnet[fd]] = -2;
 		return;
-	/* ========================= only linker ========================= */
+	}
+printbuffer:
+	printf("%d: %s\n", fd, backup);
+}
 
-	} else if (strcmp(cmd, "PRIVMSG") == 0) {
-		char msg[BUFFER_LEN] = "";
-		int *fds;
+void
+net_add(char *name, char *symb, char *host, char *port, char *chan)
+{
+	int	 i, fd;
+	Network *n;
 
-		if (fdtype[fd] == FD_TYPE_LINKER) {
-			append_netsymb(nick, fd);
-			if ((fds = htsearch(users, nick)) == NULL)
-				return;
+	/* if name or symbol or configuration already exists */
+	for (i = 0; i < netlen; i++) {
+		if (strcmp(networks[i].name, name) == 0) {
+			printf("error: network name '%s' already exists\n", name);
+			return;
+		}
+		if (strcmp(networks[i].symb, symb) == 0) {
+			printf("error: network symbol '%s' already exists\n", symb);
+			return;
+		}
+		if ((strcmp(networks[i].host, host) == 0)
+		&& (strcmp(networks[i].port, port) == 0)
+		&& (strcmp(networks[i].chan, chan) == 0)) {
+			printf("error: network configuration already exists\n");
+			return;
+		}
+	}
 
-			split(&buf, ':');	/* set buf to msg */
-			privmsg_update(msg, buf, fd);
-			for (i = 0; i < netlen; i++) {
-				if (fds[i] > 0)
-					ircsend(fds[i], "PRIVMSG %s :%s", networks[i].chan, msg);
-			}
-		} else {
-			char *netsymb;
-			char *user = split(&buf, ' ');
+	/* resize if no space */
+	if (netlen == netcap)
+		net_resize();
 
-			/* ignore messages from channel (it is handled by linker) */
-			if ((user[0] == '#') || (user[0] == '&'))
-				return;
+	/* connect */
+	if ((fd = fd_add(host, port)) == -1)
+		return;
+	/* send NICK and USER commands */
+	fdprintf(fd, "NICK linker\r\n");
+	fdprintf(fd, "USER linker 0 * :linker\r\n");
+	/* add a network */
+	fdnet[fd] = netlen;
+	fdtype[fd] = FD_TYPE_LINKER;
 
-			append_netsymb(nick, fd);
-			if ((fds = htsearch(users, nick)) == NULL)
-				return;
+	n = &networks[netlen];
+	n->fd = fd;
+	n->name = strdup(name);
+	n->symb = strdup(symb);
+	n->host = strdup(host);
+	n->port = strdup(port);
+	n->chan = strdup(chan);
+	netlen++;
+	printf("%d: network '%s' added\n", fd, name);
+}
 
-			/* split user nick and network symbol */
-			*strrchr(user, ']') = '\0';
-			netsymb = strrchr(user, '[');
-			*netsymb++ = '\0';
+void
+net_del(char *name)
+{
+	int	i, index, *fds;
+	char	quit_msg [BUFFER_LEN];
 
-			/* get the network index */
-			for (i = 0; i < netlen; i++) {
-				if (strcmp(netsymb, networks[i].symb) == 0)
-					break;
-			}
+	Htiter	it	= {0};	/* current iterator */
+	Htiter	lastit	= {0};	/* last iterator */
 
-			split(&buf, ':');	/* set buf to msg */
-			privmsg_update(msg, buf, fd);
-			ircsend(fds[i], "PRIVMSG %s :%s", user, msg);
+	/* check if the name exists */
+	index = -1;
+	for (i = 0; i < netlen; i++) {
+		if (strcmp(name, networks[i].name) == 0) {
+			index = i;
+			break;
 		}
+	}
+	if (index == -1) {
+		printf("error: network name '%s' doesn't exist\n", name);
 		return;
 	}
 
-printbuffer:
-	printf("%d: %s\n", fd, backup);
+	/* set the quit msg */
+	snprintf(quit_msg, BUFFER_LEN, "unlinking network %s", name);
+
+	/* set all fds with last network index to the current index. */
+	for (i = 0; i < nfds; i++) {
+		if (fdnet[i] == netlen-1)
+			fdnet[i] = index;
+	}
+
+	while (htiterate(users, &it)) {
+		fds = (int *)it.node->val;
+		/* delete the user */
+		if (fds[index] == -1) {
+			user_del(it.node->key, quit_msg);
+			/* this node is deleted and has no next */
+			it = lastit;
+		/* delete the clone */
+		} else {
+			if (fds[index] > 0)
+				fd_del(fds[index], quit_msg);
+			/* swap last index with the index */
+			fds[index] = fds[netlen-1];
+			fds[netlen-1] = 0;
+		}
+		lastit = it;
+	}
+
+	fd_del(networks[index].fd, quit_msg);
+	net_del_raw(index);
+	printf("%d: network '%s' deleted\n", networks[index].fd, name);
+	/* swap the network with the last */
+	networks[index] = networks[netlen-1];
+	netlen--;
 }
 
 void
-handle_fifo(int fd)
+net_del_raw(int index)
 {
-	char buffer[BUFFER_LEN];
-	char *buf;
-	char *cmd;
+	Network *n = &networks[index];
+	free(n->name);
+	free(n->symb);
+	free(n->host);
+	free(n->port);
+	free(n->chan);
+}
 
-	if (dreadline(fd, buffer, sizeof(buffer)) == -1)
-		fatal("error: dreadline() failed");
+void
+net_resize(void)
+{
+	Htiter	 it = {0};
 
-	/* printf("fifo: %s\n", buffer); */
-
-	buf = buffer;
-	cmd = split(&buf, ' ');
-	if (strcmp(cmd, "netadd") == 0) {
-		char *name = split(&buf, ' ');
-		char *symb = split(&buf, ' ');
-		char *host = split(&buf, ' ');
-		char *port = split(&buf, ' ');
-		char *chan = buf;
-		if ((*name == '\0') ||  (*symb == '\0') ||  (*host == '\0') ||  (*port == '\0') || (*chan == '\0'))
-			printf("usage: netadd <name> <symbol> <hostname> <port> <channel>\n");
-		netadd(name, symb, host, port, chan);
-	} else if (strcmp(cmd, "netdel") == 0) {
-		char *name = buf;
-		if (*name == '\0')
-			printf("usage: netdel <name>\n");
-		netdel(name);
-	} else if (strcmp(cmd, "print") == 0) {
-		print_table();
-	} else if (strcmp(cmd, "users") == 0) {
-		print_users();
-	} else if (strcmp(cmd, "exit") == 0) {
-		isrunning = 0;
-	} else {
-		printf("error: %s is not a command\n", cmd);
+	/* resize networks array */
+	if ((networks = realloc(networks, ((size_t)netcap + NET_ADDEND) * sizeof(Network))) == NULL)
+		goto error;
+	memset(networks + netcap, 0, NET_ADDEND * sizeof(Network));
+	/* also resize all the user's array */
+	while (htiterate(users, &it)) {
+		if ((it.node->val = realloc(it.node->val, ((size_t)netcap + NET_ADDEND) * sizeof(int))) == NULL)
+			goto error;
+		memset((int *)it.node->val + netcap, 0, NET_ADDEND * sizeof(int));
 	}
+	netcap += NET_ADDEND;
+	return;
+error:
+	printf("error: realloc: %s\n", strerror(errno));
+	terminate(1);
 }
 
-int
-main(int argc, char *argv[])
+void
+net_update(void)
 {
-	int		fifofd, r, i;
-	fd_set		rdset;
-	struct stat	fifo_st;
+	int	*fds;
+	Htiter	 it = {0};
 
-	/* check arguments */
-	if (argc != 2)
-		fatal("usage: %s <fifo>", argv[0]);
+	while (htiterate(users, &it)) {
+		fds = (int *)it.node->val;
+		/* netlen-1 can be occupied if networks are added quickly
+		 * and has not enough time to connect to it's server */
+		if (fds[netlen-1] == 0) {
+			fds[netlen-1] = clone_add(netlen-1, it.node->key);
+		}
+	}
+}
 
-	/* warning */
-	printf("WARNING: This software is provided 'as-is', without any express or "
-		"implied warranty.\nIn no event shall the author(s) be liable "
-		"for any damages arising from the use of this software.\n");
+void
+user_add(char *unick, int nfd)
+{
+	int i, *fds;
+	Network *n;
+	size_t len;
+	char *nick;
 
-	/* init networks and users */
-	networks = ecalloc((size_t)netcap, sizeof(Network));
-	users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND);
-	/* init fdnet array to -1 */
-	for (i = 0; i < MAX_FD; i++)
-		fdnet[i] = -1;
+	n = &networks[fdnet[nfd]];
+	len = strlen(unick) + strlen(n->symb) + 2 + 1;
 
-	/* crate or/and open fifo */
-	if (stat(argv[1], &fifo_st) == 0) {
-		if (!(fifo_st.st_mode & S_IFIFO))
-			fatal("error: file %s is not a fifo", argv[1]);
-	} else if (mkfifo(argv[1], S_IRWXU) != 0) {
-		fatal("error: cannot create fifo file %s", argv[1]);
+	/* too long nick */
+	if (len-1 > MAX_NICK_LEN) {
+		printf("error: user nick '%s' is too big\n", unick);
+		return;
 	}
-	fifofd = open(argv[1], O_RDWR);
-	if (fifofd < 0)
-		fatal("error: cannot open() '%s'", argv[1]);
 
-	/* set stdout to unbufferd */
-	setvbuf(stdout, NULL, _IONBF, 0);
-	/* initialize fdset */
-	FD_ZERO(&fdset);
-	FD_SET(fifofd, &fdset);
-	nfds = fifofd + 1;
+	/* resize hash table if store is low */
+	if ((users->cap - users->len) < USER_ADDEND)
+		htresize(users, users->cap + USER_ADDEND);
 
-	/* select loop */
-	while (isrunning) {
-		rdset = fdset;
-		r = select(nfds, &rdset, 0, 0, NULL);
-		if (r < 0) {
-			if (errno == EINTR)
-				continue;
-			fatal("error: select: %s", strerror(errno));
-		} else if (r == 0) {
-			fatal("error: select() timeout");
+	/* allocate a new user */
+	nick = ecalloc(len, sizeof(char));
+	fds = ecalloc((size_t)netcap, sizeof(int));
+	sprintf(nick, "%s[%s]", unick, n->symb);
+
+	/* clone the user on all other network */
+	for (i = 0; i < netlen; i++) {
+		/* set -1 on user's network */
+		if (i == fdnet[nfd]) {
+			fds[i] = -1;
+		} else {
+			fds[i] = clone_add(i, nick);
 		}
+	}
+	/* insert it to the users hash table */
+	if (htinsert(users, nick, fds) == -1) {
+		/* this shouldn't happen as it was already checked */
+		printf("error: user '%s' already exists\n", nick);
+		terminate(1);
+	}
+}
 
-		for (i = 0; i < nfds; i++) {
-			if (FD_ISSET(i, &rdset)) {
-				if (i == fifofd)
-					handle_fifo(i);
-				else
-					handle_server(i);
+void
+user_del(char *nick, char *msg)
+{
+	int *fds, i;
+	if ((fds = htsearch(users, nick)) == NULL) {
+		printf("error: user '%s' doesn't exits\n", nick);
+		return;
+	}
 
-				/* handle one fd at one time because FD_CLR */
-				break;
-			}
+	for (i = 0; i < netlen; i++) {
+		if (fds[i] > 0)
+			fd_del(fds[i], msg);
+	}
+	htremove(users, nick);
+}
+
+int *
+user_fds(char *nick, int nfd)
+{
+	unsigned int suf;
+	int *fds = NULL;
+
+	/* count suffix */
+	for (suf = 0; nick[strlen(nick)-suf-1] == '_'; suf++);
+	/* remove suffix */
+	if (suf > 0)
+		nick[strlen(nick)-suf] = '\0';
+
+	fds = htsearch(users, nick);
+	/* if match but suffix doesn't match */
+	if ((fds != NULL) && (fdsuf[fds[fdnet[nfd]]] != (int)suf))
+		fds = NULL;
+
+	/* add back suffix */
+	if (suf > 0)
+		nick[strlen(nick)] = '_';
+
+	return fds;
+}
+
+int
+clone_add(int netindex, char *nick)
+{
+	int fd;
+	Network *n = &networks[netindex];
+
+	if ((fd = fd_add(n->host, n->port)) == -1)
+		return -1;
+	/* send NICK and USER commands */
+	fdprintf(fd, "NICK %s\r\n", nick);
+	fdprintf(fd, "USER %s 0 * :%s\r\n", n->name, n->name);
+	/* add a user */
+	fdnet[fd] = netindex;
+	fdtype[fd] = FD_TYPE_CLONE;
+	fdsuf[fd] = 0;
+	return fd;
+}
+
+int
+fd_add(char *host, char *port)
+{
+	int fd;
+	if ((fd = dial(host, port)) == -1)
+		return -1;
+
+	FD_SET(fd, &fdset);
+	if (fd+1 > nfds)
+		nfds = fd+1;
+
+	return fd;
+}
+
+void
+fd_del(int fd, char *msg)
+{
+	fdprintf(fd, "QUIT :%s\r\n", msg);
+	close(fd);
+	FD_CLR(fd, &fdset);
+	fdnet[fd] = -1;
+}
+
+void
+nick_add_symb(char *nick, int fd)
+{
+	strcat(nick, "[");
+	strcat(nick, networks[fdnet[fd]].symb);
+	strcat(nick, "]");
+}
+
+/*
+ * trim all the nicknames to original nick
+ * src will be destructed
+ */
+void
+privmsg_update(char *dst, char *src, int fd)
+{
+	char d;		/* delimiter */
+	char *n;
+
+	while (src != NULL) {
+		n = strpbrk(src, " :;,<>@&~%+\\");
+		if (n == NULL) {
+			d = '\0';
+		} else {
+			d = *n;
+			*n = '\0';
+			n++;
 		}
+
+		/* check if the word is nick */
+		if (user_fds(src, fd) != NULL)
+			*strrchr(src, '[') = '\0';
+
+		strcat(dst, src);
+		strncat(dst, &d, 1);
+
+		src = n;
 	}
+}
 
+void
+terminate(int status)
+{
+	int i;
 	for (i = 0; i < nfds; i++) {
-		if (fdnet[i] > 0) {
-			printf("shutting down %d\n", i);
-			fddel(i, "linker shutting down");
+		if (fdnet[i] >= 0) {
+			/* printf("%d: shutting down\n", i); */
+			fd_del(i, "linker shutting down");
 		}
 	}
 	/* delete all the users */
@@ -751,8 +767,92 @@
 
 	/* delete all the networks */
 	for (i = 0; i < netlen; i++)
-		__netdel(i);
+		net_del_raw(i);
 
 	free(networks);
-	return 0;
+	if (status == 0)
+		printf("exit successfully\n");
+	else
+		printf("aborted\n");
+	exit(1);
+}
+
+void
+print_table(void)
+{
+	int	i, *fds, diff, tabs;
+	Htiter	it = {0};
+	char	*nick;
+
+	if (netlen == 0)
+		return;
+
+	print_border();
+	/* print networks */
+	printf("Networks\t\t");
+	for (i = 0; i < netlen; i++)
+		printf("%s(%d)\t", networks[i].symb, networks[i].fd);
+	printf("\n");
+
+	while (htiterate(users, &it)) {
+		fds  = (int *)it.node->val;
+		nick = (char *)it.node->key;
+		/* print tabbed user nick */
+		printf("%s", nick);
+		diff = 24 - (int)strlen(nick);
+		tabs = ((diff / 8) + (diff % 8 > 0));
+		printf("%.*s", tabs, "\t\t\t");
+		/* print tabbed clones fds */
+		for (i = 0; i < netlen; i++) {
+			printf("%d", fds[i]);
+			/* print suffix */
+			if ((fds[i] > 0) && (fdsuf[fds[i]] > 0))
+				printf("(%d)", fdsuf[fds[i]]);
+			printf("\t");
+		}
+		printf("\n");
+	}
+	print_border();
+}
+
+void
+print_htable(void)
+{
+	Htiter	it = {0};
+	int	index = -1;
+
+	print_border();
+	while (htiterate(users, &it)) {
+		if (index != (int)it.index) {
+			/* ignore first new line */
+			if (index != -1)
+				printf("\n");
+			printf("%d", it.index);
+			index = (int)it.index;
+		}
+		printf(" -> %s", (char *)it.node->key);
+	}
+	printf("\n");
+	print_border();
+}
+
+void
+print_users(void)
+{
+	Htiter	it = {0};
+	int i = 0;
+
+	print_border();
+	while (htiterate(users, &it))
+		printf("%d: %s\n", i++, (char *)it.node->key);
+	print_border();
+}
+
+void
+print_border(void)
+{
+	int i;
+	for (i = 0; i < 64; i++)
+		printf("-");
+	printf("\n");
 }
--- /dev/null
+++ b/util.c
@@ -1,0 +1,114 @@
+/*
+ * This work is dedicated to the public domain.
+ * See COPYING file for more information.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+	void *p;
+	if ((p = calloc(nmemb, size)) == NULL) {
+		printf("error: calloc: %s\n", strerror(errno));
+		return NULL;
+	}
+	return p;
+}
+
+char*
+split(char **str, char ch)
+{
+	char *token = *str;
+
+	if (**str == '\0') return *str;
+
+	while (**str != ch && **str != '\0')
+		(*str)++;
+	if (**str == '\0')
+		return token;
+	**str = '\0';
+	(*str)++;
+	while(**str == ch && **str != '\0')
+		(*str)++;
+	return token;
+}
+
+ssize_t
+fdprintf(int fd, const char *fmt, ...)
+{
+	va_list args;
+	char buf[1024];
+	size_t len;
+	ssize_t n, i = 0;
+
+	va_start(args, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+	len = strlen(buf);
+	while (i < (ssize_t)len) {
+		if ((n = write(fd, buf + i, len - (size_t)i)) == -1) {
+			printf("error: write failed: %s\n", strerror(errno));
+			return -1;
+		}
+		i += n;
+	}
+	return i;
+}
+
+int
+dial(char *host, char *port)
+{
+	static struct addrinfo hints, *res = NULL, *res0;
+	int fd = -1, r;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+
+	if ((r = getaddrinfo(host, port, &hints, &res0)) != 0) {
+		printf("error: getaddrinfo: %s\n", gai_strerror(r));
+		return -1;
+	}
+	for (res = res0; res != NULL; res = res->ai_next) {
+		if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
+			continue;
+		if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
+			close(fd);
+			fd = -1;
+			continue;
+		}
+		break;
+	}
+	if (fd == -1)
+		printf("error: cannot connect to host '%s'\n", host);
+
+	freeaddrinfo(res0);
+	return fd;
+}
+
+ssize_t
+readline(int fd, char *buffer, size_t size)
+{
+	ssize_t n, i = 0;
+	char c;
+
+	do {
+		if ((n = read(fd, &c, sizeof(char))) != sizeof(char))
+			return n;
+		buffer[i++] = c;
+	} while ((i < (ssize_t)size) && (c != '\n') && (c != '\0'));
+
+	buffer[i-1] = '\0';
+	return i;
+}
--- a/utils.c
+++ /dev/null
@@ -1,152 +1,0 @@
-/*
- * This work is dedicated to the public domain.
- * See COPYING file for more information.
- */
-
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <unistd.h>
-
-#include "utils.h"
-
-void
-fatal(const char *fmt, ...)
-{
-	va_list ap;
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-
-	if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
-		fputc(' ', stderr);
-		perror(NULL);
-	} else {
-		fputc('\n', stderr);
-	}
-	exit(1);
-}
-
-void *
-ecalloc(size_t nmemb, size_t size)
-{
-	void *p;
-	if ((p = calloc(nmemb, size)) == NULL)
-		fatal("calloc:");
-	return p;
-}
-
-char*
-split(char **str, char ch)
-{
-	char *token = *str;
-
-	if (**str == '\0') return *str;
-
-	while (**str != ch && **str != '\0')
-		(*str)++;
-	if (**str == '\0')
-		return token;
-	**str = '\0';
-	(*str)++;
-	while(**str == ch && **str != '\0')
-		(*str)++;
-	return token;
-}
-
-void
-ircsend(int fd, const char *format, ...)
-{
-	static char buf[4096];
-	va_list args;
-	va_start(args, format);
-	vsnprintf(buf, sizeof(buf), format, args);
-	va_end(args);
-	strcat(buf, "\r\n");
-	send(fd, buf, strlen(buf), 0);
-}
-
-int
-dial(char *host, char *port)
-{
-	static struct addrinfo hints, *res = NULL, *res0;
-	int fd = -1, r;
-
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-
-	if ((r = getaddrinfo(host, port, &hints, &res0)) != 0) {
-		printf("error: getaddrinfo: %s\n", gai_strerror(r));
-		return -1;
-	}
-
-	for (res = res0; res != NULL; res = res->ai_next) {
-		if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
-			continue;
-		if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
-			close(fd);
-			fd = -1;
-			continue;
-		}
-		break;
-	}
-	if (fd == -1) {
-		printf("error: cannot connect to host '%s'\n", host);
-		return -1;
-	}
-	freeaddrinfo(res0);
-	return fd;
-}
-
-int
-isstrnumber(char *str)
-{
-	int i;
-	for (i = 0; str[i]!= '\0'; i++) {
-		if (isdigit(str[i]) == 0)
-			return 0;
-	}
-	return 1;
-}
-
-size_t
-strlcpy(char *dst, const char *src, size_t size)
-{
-	size_t i;
-
-	if (size == 0)
-		return 0;
-
-	for (i = 0; i < size; i++) {
-		dst[i] = src[i];
-		if (src[i] == '\0')
-			return i;
-	}
-
-	dst[size-1] = '\0';
-	return size-1;
-}
-
-
-ssize_t
-dreadline(int fd, char *buffer, size_t size)
-{
-	size_t i = 0;
-	char c;
-	ssize_t l;
-
-	do {
-		if ((l = read(fd, &c, sizeof(char))) != sizeof(char))
-			return l;
-		buffer[i++] = c;
-	} while ((i < size) && (c != '\n') && (c != '\0'));
-
-	buffer[i-1] = '\0';
-	return (ssize_t)i;
-}
--- a/utils.h
+++ /dev/null
@@ -1,13 +1,0 @@
-/*
- * This work is dedicated to the public domain.
- * See COPYING file for more information.
- */
-
-void	 fatal(const char *fmt, ...);
-void	*ecalloc(size_t nmemb, size_t size);
-char	*split(char **str, char ch);
-void	 ircsend(int fd, const char *format, ...);
-int	 dial(char *host, char *port);
-int	 isstrnumber(char *str);
-size_t	 strlcpy(char *dst, const char *src, size_t size);
-ssize_t	 dreadline(int fd, char *buffer, size_t size);