ref: 49ca82832b3505a7ce86e96f11a90599219afaab
dir: /main.c/
/* * This work is dedicated to the public domain. * See COPYING file for more information. */ #include <errno.h> #include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <time.h> #include <unistd.h> #ifdef __gnu_linux__ #include <sys/epoll.h> #include <bsd/err.h> #include <bsd/stdlib.h> #include <bsd/string.h> #else #include <sys/event.h> #endif #include "htable.h" #include "util.c" #define FALSE 0 #define TRUE 1 #define EV_READ 1 #define EV_WRITE 2 #define EVENT_ADDEND 100 #define NET_ADDEND 10 #define USER_ADDEND 100 #define PING_TIMEOUT 240 #define CLONE_COOLDOWN 1 #define CLONE_ADDEND 10 #define BUFSIZE 1024 #define NICK_LEN 16 #define MAX_EVENTS 10 enum { IDLE = 0, RESET, CLONING }; struct network { int id; /* event index */ char *name; /* name */ char *symb; /* symbol */ char *host; /* host */ char *port; /* port */ char *chan; /* channel */ int ready; /* joined */ }; struct event { int fd; /* event fd */ int netid; /* net index */ char *user; /* user nick */ int suffix; /* suffix count */ time_t time; /* last response */ int ready; /* joined */ }; static int debug; static int done; /* program state */ static int fifofd; /* fifo fd */ static char *fifopath; /* fifo path */ static char msg [BUFSIZE]; /* message buffer */ static int timeout_id = -1; #ifdef __gnu_linux__ static int epfd; /* epoll instance */ #else static int kqfd; /* kqueue instance */ #endif static int *fdtoid; /* fd to event id */ static int fdslen; /* maximum fd */ static struct event *events; /* events array */ static int evslen; /* array length */ static int evscap; /* array capacity */ static struct network *networks; /* networks array */ static int netlen; /* array length */ static int netcap; /* array capacity */ static int state = IDLE; /* * 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; /* functions prototype */ void fd_register(int, int); void fifo_read(void); time_t event_timeout(void); void event_write(int); int event_read(int); void net_add(char *, char *, char *, char *, char *); void net_del(char *); void net_del_raw(int); void net_users_add(int, char *); void user_add(char *, int, int); void user_del(char *, char *); int *user_event_ids(char *, int); int clone_add(char *, int); int event_add(int, int, char *); void event_del(int); void nick_add_symb(char *, int); void privmsg_update(char *, char *, int); void print_table(void); void print_htable(void); void print_users(void); void print_border(void); int main(int argc, char *argv[]) { #ifdef __gnu_linux__ struct epoll_event epevs [MAX_EVENTS]; /* maximum event to handle */ #else struct kevent kevs [MAX_EVENTS]; /* maximum event to handle */ struct timespec tmsp; #endif int i, fd, id, nev; time_t timeout; time_t ntime; /* next timeout time */ /* set stdout to unbufferd */ setvbuf(stdout, NULL, _IONBF, 0); /* check arguments */ if (argc != 2) { printf("usage: %s fifo\n", getprogname()); return 0; } else { fifopath = argv[1]; } /* init global variables */ fdslen = evscap = EVENT_ADDEND; netcap = NET_ADDEND; fdtoid = ecalloc((size_t)fdslen, sizeof(int)); events = ecalloc((size_t)evscap, sizeof(struct event)); networks = ecalloc((size_t)netcap, sizeof(struct network)); users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND); #ifdef __gnu_linux__ if ((epfd = epoll_create1(0)) == -1) err(1, "epoll_create1"); #else if ((kqfd = kqueue()) == -1) err(1, "kqueue"); #endif fifofd = fifo_open(fifopath); fd_register(fifofd, EV_READ); ntime = -1; /* event loop */ while (!done) { if (ntime == -1) timeout = -1; else if ((timeout = ntime - time(NULL)) < 0) timeout = 0; #ifdef __gnu_linux__ if ((nev = epoll_wait(epfd, epevs, MAX_EVENTS, (int)timeout * 1000)) == -1) err(1, "epoll_wait"); #else tmsp.tv_sec = timeout; tmsp.tv_nsec = 0; if ((nev = kevent(kqfd, NULL, 0, kevs, MAX_EVENTS, timeout < 0 ? NULL : &tmsp)) == -1) err(1, "kevent"); #endif if (nev == 0) { ntime = event_timeout(); continue; } if (debug) { printf("fds:"); for (i = 0; i < nev; i++) { printf(" %d", epevs[i].data.fd); } printf("\n"); } for (i = 0; i < nev; i++) { #ifdef __gnu_linux__ fd = epevs[i].data.fd; #else fd = (int)kevs[i].ident; #endif id = fdtoid[fd]; if (fd == fifofd) { fifo_read(); #ifdef __gnu_linux__ } else if (epevs[i].events & EPOLLOUT) { #else } else if (kevs[i].filter == EVFILT_WRITE) { #endif event_write(id); #ifdef __gnu_linux__ } else if (epevs[i].events & EPOLLIN) { #else } else if (kevs[i].filter == EVFILT_READ) { #endif if (event_read(id)) { timeout_id = -1; ntime = 0; } } else { errx(1, "unknown event"); } } } /* * cleanup */ snprintf(msg, sizeof(msg), "QUIT :linker shutting down\r\n"); for (i = 0; i < evslen; i++) { writeall(events[i].fd, msg); close(events[i].fd); } free(fdtoid); free(events); /* delete all the networks */ for (i = 0; i < netlen; i++) net_del_raw(i); free(networks); /* delete all the users */ htdestroy(users); return 0; } void fd_register(int fd, int type) { #ifdef __gnu_linux__ struct epoll_event ev; if (type == EV_READ) ev.events = EPOLLIN; else if (type == EV_WRITE) ev.events = EPOLLOUT; else errx(1, "unkown event type"); ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) err(1, "epoll_ctl"); #else struct kevent ev; int filter; if (type == EV_READ) filter = EVFILT_READ; else if (type == EV_WRITE) filter = EVFILT_WRITE; else errx(1, "unkown event type"); EV_SET(&ev, fd, filter, EV_ADD, 0, 0, 0); if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) err(1, "kevent"); #endif /* printf("added fd: %d\n", fd); */ } void fifo_read(void) { char buffer [BUFSIZE]; char *buf; char *cmd; ssize_t n; n = readline(fifofd, buffer, sizeof(buffer)); if (n == -1) { err(1, "fifo: read"); } else if (n == 0) { /* reopen fifo again */ close(fifofd); fifofd = fifo_open(fifopath); fd_register(fifofd, EV_READ); return; } 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> <host> <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) { done = TRUE; } else { warnx("%s is not a command", cmd); } } time_t event_timeout(void) { static Htiter it = {0}; int i, j, *ids; char *nick; struct network *n; time_t ntime; /* if (timeout_id != -1) { printf("%d: PING\n", events[timeout_id].fd); snprintf(msg, sizeof(msg), "PING %s\r\n", networks[events[timeout_id].netid].host); writeall(events[timeout_id].fd, msg); events[timeout_id].time = time(NULL); } else { */ if (state == RESET) { state = CLONING; it.index = 0; it.node = NULL; } printf("Adding %d bot\n", CLONE_ADDEND); j = 0; while (htiterate(users, &it)) { nick = (char *)it.node->key; ids = (int *)it.node->val; for (i = 0; i < netlen; i++) { n = &networks[i]; if (!n->ready || ids[i] != 0) continue; ids[i] = clone_add(nick, i); printf("%d: %s\n", events[ids[i]].fd, nick); } if (++j >= CLONE_ADDEND) goto calculate; } state = IDLE; it.index = 0; it.node = NULL; /* } */ calculate: ntime = -1; timeout_id = -1; if (state == CLONING) ntime = time(NULL) + CLONE_COOLDOWN; /* for(i = 0; i < evslen; i++) { if ((events[i].time != 0) && ((ntime == -1) || (events[i].time + PING_TIMEOUT < ntime))) { ntime = events[i].time + PING_TIMEOUT; timeout_id = i; } } */ return ntime; } void event_write(int id) { int fd = events[id].fd; #ifdef __gnu_linux__ struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) err(1, "epoll_ctl"); #else struct kevent ev; EV_SET(&ev, fd, EV_WRITE, EV_DISABLE, 0, 0, 0); if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) err(1, "kevent"); fd_register(fd, EV_READ); #endif 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); } events[id].time = time(NULL); } int event_read(int id) { char buffer [BUFSIZE]; char backup [BUFSIZE]; char lnick [NICK_LEN]; /* linker nick */ char *buf; char *cmd; char *nick; int i, fd, netid; ssize_t n; fd = events[id].fd; netid = events[id].netid; events[id].time = time(NULL); n = readline(fd, buffer, sizeof(buffer)); if (n == -1) { warn("%d: read", fd); event_del(id); return 0; } else if (n == 0) { warnx("%d: connection closed", fd); event_del(id); return 0; } if (!*buffer) return 0; /* clone the buffer */ strlcpy(backup, buffer, sizeof(buffer)); buf = buffer; /* set linker nick */ strlcpy(lnick, "linker", sizeof(lnick)); for (i = 0; i < events[id].suffix; i++) strcat(lnick, "_"); /* first column */ cmd = split(&buf, ' '); if (strcmp(cmd, "NOTICE") == 0) { return 0; } else if (strcmp(cmd, "ERROR") == 0) { goto printbuffer; } else if (strcmp(cmd, "PING") == 0) { snprintf(msg, sizeof(msg), "PONG %s\r\n", buf); writeall(fd, msg); goto printbuffer; } /* strip nick from first column */ nick = split(&cmd, '!'); if (nick[0] == ':') nick++; /* second column */ cmd = split(&buf, ' '); /* ignore all the info messages */ if ((strcmp(cmd, "002") == 0) || (strcmp(cmd, "003") == 0) || (strcmp(cmd, "004") == 0) || (strcmp(cmd, "005") == 0) || (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) || (strcmp(cmd, "266") == 0) || (strcmp(cmd, "250") == 0) || (strcmp(cmd, "332") == 0) || (strcmp(cmd, "333") == 0) || (strcmp(cmd, "375") == 0) || (strcmp(cmd, "372") == 0) || (strcmp(cmd, "376") == 0) || (strcmp(cmd, "396") == 0) || (strcmp(cmd, "366") == 0) || (strcmp(cmd, "MODE") == 0) || (strcmp(cmd, "NOTICE") == 0)) { return 0; } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ split(&buf, ' '); nick = split(&buf, ' '); if (strlen(nick)+1 > NICK_LEN) { warnx("nick '%s' is too big", nick); if (strcmp(nick, lnick) == 0) { net_del(networks[netid].name); } else { snprintf(msg, sizeof(msg), "QUIT :nick is too big\r\n"); user_del(nick, msg); } } else { strcat(nick, "_"); events[id].suffix++; snprintf(msg, sizeof(msg), "NICK %s\r\n", nick); writeall(fd, msg); } return 0; } else if (strcmp(cmd, "001") == 0) { snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan); writeall(fd, msg); return 0; } else if (strcmp(cmd, "PRIVMSG") == 0) { char privmsg [BUFSIZE] = ""; int *ids; if (events[id].user == NULL) { /* if linker */ nick_add_symb(nick, netid); if ((ids = htsearch(users, nick)) == NULL) return 0; split(&buf, ':'); /* set buf to msg */ privmsg_update(privmsg, buf, netid); for (i = 0; i < netlen; i++) { if (ids[i] > 0) { snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg); writeall(events[ids[i]].fd, msg); } } } else { char *netsymb; char *user = split(&buf, ' '); /* ignore messages from channel (it is handled by linker) */ if ((user[0] == '#') || (user[0] == '&')) return 0; nick_add_symb(nick, netid); if ((ids = htsearch(users, nick)) == NULL) return 0; /* 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(privmsg, buf, netid); snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg); writeall(events[ids[i]].fd, msg); } return 0; } /* these messages are handled by linker */ if (events[id].user != NULL) { /* if clone */ if (strcmp(cmd, "353") == 0) { events[id].ready = TRUE; return 0; } else if ((strcmp(cmd, "JOIN") == 0) || (strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0) || (strcmp(cmd, "KICK") == 0) || (strcmp(cmd, "NICK") == 0)) { return 0; } } else if (strcmp(cmd, "353") == 0) { char *nick; split(&buf, ':'); networks[netid].ready = TRUE; state = RESET; /* then add all new users */ while (*(nick = split(&buf, ' ')) != '\0') { if (*nick == '@' || *nick == '&' || *nick == '~' || *nick == '%' || *nick == '+' || *nick == '\\') nick++; if (strcmp(nick, lnick) != 0) user_add(nick, netid, FALSE); } return 1; } else if (strcmp(cmd, "JOIN") == 0) { if ((strcmp(nick, lnick) != 0) && (user_event_ids(nick, netid) == NULL)) { /* if not clone */ if (state != IDLE) warn("ignoring user '%s' due to network cloning", nick); else user_add(nick, netid, TRUE); } return 0; } else if ((strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0)) { snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf); nick_add_symb(nick, netid); if (htsearch(users, nick) != NULL) { printf("userdel: %s\n", nick); user_del(nick, msg); } return 0; } else if (strcmp(cmd, "NICK") == 0) { int *ids, i; char *newnick; nick_add_symb(nick, netid); if ((ids = htsearch(users, nick)) == NULL) return 0; /* set buf to new nick */ split(&buf, ':'); /* allocate a newnick and append the netsym and then replace the old */ newnick = ecalloc(strlen(buf) + strlen(networks[netid].symb) + 2 + 1, sizeof(char)); sprintf(newnick, "%s[%s]", buf, networks[netid].symb); htsetkey(users, nick, newnick); snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick); for (i = 0; i < netlen; i++) { if (ids[i] > 0) writeall(events[ids[i]].fd, msg); } return 0; } else if (strcmp(cmd, "KICK") == 0) { /* :<nick_which_is_kicking>!~user@host KICK <channel> <nick_which_has_been_kicked> :<kick_msg> */ int *ids; char *chan = split(&buf, ' '); char *user = split(&buf, ' '); /* set the quit msg */ snprintf(msg, sizeof(msg), "QUIT : kicked by %s\r\n", nick); /* delete whole network if it is the linker */ if (strcmp(user, lnick) == 0) { net_del(networks[netid].name); return 0; } /* delete the user if the message from the same network */ if ((ids = user_event_ids(user, netid)) == NULL) { nick_add_symb(user, netid); user_del(user, msg); return 0; } /* close the kicked fd */ writeall(fd, msg); event_del(id); /* * send notice in the channel through linker */ /* get the original user netid */ for (i = 0; i < netlen; i++) { if (ids[i] == -1) break; } /* set buf to msg */ split(&buf, ':'); /* remove netsymb and suffix */ *strrchr(user, '[') = '\0'; /* send notice */ 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(events[networks[i].id].fd, msg); return 0; } printbuffer: return 0; } void net_add(char *name, char *symb, char *host, char *port, char *chan) { struct network *n; int i, fd; /* if name, symbol or configuration already exists */ for (i = 0; i < netlen; i++) { if (strcmp(networks[i].name, name) == 0) { warnx("network name '%s' already exists", name); return; } if (strcmp(networks[i].symb, symb) == 0) { warnx("network symbol '%s' already exists", symb); return; } if ((strcmp(networks[i].host, host) == 0) && (strcmp(networks[i].port, port) == 0) && (strcmp(networks[i].chan, chan) == 0)) { warnx("network configuration already exists"); return; } } /* resize if full */ if (netlen == netcap) { Htiter it = {0}; networks = realloc0(networks, sizeof(struct network) * (size_t)netcap, sizeof(struct network) * (size_t)(netcap + NET_ADDEND)); while (htiterate(users, &it)) it.node->val = realloc0(it.node->val, sizeof(int) * (size_t)netcap, sizeof(int) * (size_t)(netcap + NET_ADDEND)); netcap += NET_ADDEND; } /* connect */ if ((fd = dial(host, port)) == -1) return; /* add a network */ n = &networks[netlen]; n->id = event_add(fd, netlen, NULL); n->name = strdup(name); n->symb = strdup(symb); n->host = strdup(host); n->port = strdup(port); n->chan = strdup(chan); n->ready = FALSE; netlen++; printf("%d: network '%s' added\n", fd, name); } void net_del(char *name) { int i, netid, *ids; Htiter it = {0}; /* current iterator */ Htiter lastit = {0}; /* last iterator */ /* get netid */ netid = -1; for (i = 0; i < netlen; i++) { if (strcmp(name, networks[i].name) == 0) { netid = i; break; } } if (netid == -1) { warnx("network '%s' doesn't exist", name); return; } /* set the quit msg */ snprintf(msg, sizeof(msg), "QUIT :unlinking network %s\r\n", name); /* reconstruct the user-clones table */ while (htiterate(users, &it)) { ids = (int *)it.node->val; /* delete all the users of deleting network */ if (ids[netid] == -1) { user_del(it.node->key, msg); /* this node is deleted */ it = lastit; /* delete the clones */ } else { if (ids[netid] > 0) { writeall(events[ids[netid]].fd, msg); event_del(ids[netid]); } /* swap last with current one */ ids[netid] = ids[netlen-1]; } lastit = it; } /* set last netid of events to current netid. */ for (i = 0; i < evslen; i++) { if (events[i].netid == netlen-1) events[i].netid = netid; } writeall(events[networks[netid].id].fd, msg); event_del(networks[netid].id); net_del_raw(netid); printf("%d: network '%s' deleted\n", events[networks[netid].id].fd, name); /* swap the network with the last */ networks[netid] = networks[netlen-1]; netlen--; } void net_del_raw(int netid) { struct network *n = &networks[netid]; free(n->name); free(n->symb); free(n->host); free(n->port); free(n->chan); } void user_add(char *unick, int netid, int clone) { size_t len; char *nick; int *ids, i; len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1; /* too long nick */ if (len - 1 > NICK_LEN) warnx("nick '%s' is too big", unick); /* resize hash table if storage is low */ if ((users->cap - users->len) < USER_ADDEND) htresize(users, users->cap + USER_ADDEND); /* allocate a new user */ nick = ecalloc(len, sizeof(char)); ids = ecalloc((size_t)netcap, sizeof(int)); sprintf(nick, "%s[%s]", unick, networks[netid].symb); ids[netid] = -1; printf("useradd: %s\n", nick); if (clone) { /* clone the user on all other network */ for (i = 0; i < netlen; i++) { if (networks[i].ready == FALSE) continue; if (i != netid) { ids[i] = clone_add(nick, i); printf("%d: %s\n", events[ids[i]].fd, nick); } } } /* insert it to the users hash table */ if (htinsert(users, nick, ids) == -1) /* this shouldn't happen as it was already checked */ errx(1, "user '%s' already exists", nick); } void user_del(char *nick, char *msg) { int i, *ids; if ((ids = htsearch(users, nick)) == NULL) errx(1, "user '%s' 1 doesn't exists", nick); for (i = 0; i < netlen; i++) { if (ids[i] <= 0) continue; if (events[ids[i]].ready == TRUE) writeall(events[ids[i]].fd, msg); event_del(ids[i]); } htremove(users, nick); } int * user_event_ids(char *nick, int netid) { unsigned int s; int *ids = NULL; /* count suffix */ for (s = 0; nick[strlen(nick)-s-1] == '_'; s++); /* remove suffix */ if (s > 0) nick[strlen(nick)-s] = '\0'; ids = htsearch(users, nick); /* if match but suffix doesn't match */ if ((ids != NULL) && (events[ids[netid]].suffix != (int)s)) ids = NULL; /* add suffix back */ if (s > 0) nick[strlen(nick)] = '_'; return ids; } int clone_add(char *nick, int netid) { struct network *n; int fd; n = &networks[netid]; if ((fd = dial(n->host, n->port)) == -1) { warn("enable to connect to %s\n", n->host); return -1; } return event_add(fd, netid, nick); } int event_add(int fd, int netid, char *user) { int i = evslen; if (evslen == evscap) { events = realloc0(events, sizeof(struct event) * (size_t)evscap, sizeof(struct event) * (size_t)(evscap + EVENT_ADDEND)); evscap += EVENT_ADDEND; } events[i].fd = fd; events[i].netid = netid; events[i].user = user; events[i].suffix = 0; events[i].time = 0; events[i].ready = FALSE; fd_register(fd, EV_WRITE); if (fd == fdslen - 1) { fdtoid = realloc0(fdtoid, sizeof(int) * (size_t)fdslen, sizeof(int) * (size_t)fdslen * 2); fdslen *= 2; } fdtoid[fd] = i; return evslen++; } void event_del(int id) { int *ids; int l = evslen - 1; /* last id */ /* swap id */ if (events[l].user == NULL) { /* if linker */ networks[events[l].netid].id = id; } else { /* else user */ if ((ids = htsearch(users, events[l].user)) == NULL) errx(1, "user '%s' 2 doesn't exists", events[l].user); ids[events[l].netid] = id; } /* disable id */ if (events[id].user == NULL) { /* if linker */ networks[events[id].netid].id = -2; } else { /* else user */ if ((ids = htsearch(users, events[id].user)) == NULL) errx(1, "user '%s' 3 doesn't exists", events[id].user); ids[events[id].netid] = -2; } close(events[id].fd); events[id] = events[l]; evslen--; fdtoid[events[id].fd] = id; } void nick_add_symb(char *nick, int netid) { strcat(nick, "["); strcat(nick, networks[netid].symb); strcat(nick, "]"); } /* * trim all the nicknames to original nick * src will be destructed */ void privmsg_update(char *dst, char *src, int netid) { 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_event_ids(src, netid) != NULL) *strrchr(src, '[') = '\0'; strcat(dst, src); strncat(dst, &d, 1); src = n; } } void print_table(void) { int i, *ids, diff, tabs; Htiter it = {0}; char *nick; if (netlen == 0) return; print_border(); /* print networks */ printf("struct networks\t\t"); for (i = 0; i < netlen; i++) printf("%s(%d)\t", networks[i].symb, events[networks[i].id].fd); printf("\n"); while (htiterate(users, &it)) { ids = (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 ids */ for (i = 0; i < netlen; i++) { printf("%d", ids[i]); /* print suffix */ if ((ids[i] > 0) && (events[ids[i]].suffix > 0)) printf("(%d)", events[ids[i]].suffix); 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"); }