Track user names to properly show joins and parts - irc - A minimalistic IRC client, forked from https://c9x.me/irc/
 (HTM) git clone git://vernunftzentrum.de/irc.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit e5ae717f4ac681933c42f9bb82ffd01386545522
 (DIR) parent a68ac08f9543bbb869b49f1329fdf9281d412127
 (HTM) Author: Christian Kellermann <ckeen@pestilenz.org>
       Date:   Fri,  6 Apr 2018 23:03:04 +0200
       
       Track user names to properly show joins and parts
       
       Diffstat:
         irc.c                               |     145 +++++++++++++++++++++++++++++--
       
       1 file changed, 136 insertions(+), 9 deletions(-)
       ---
 (DIR) diff --git a/irc.c b/irc.c
       @@ -36,7 +36,8 @@
        enum {
                ChanLen = 64,
                LineLen = 512,
       -        MaxChans = 16,
       +        MaxChans = 32,
       +        MaxKnownUsers = 2048,
                BufSz = 2048,
                LogSz = 4096,
                MaxRecons = 10, /* -1 for infinitely many */
       @@ -62,6 +63,12 @@ static struct Chan {
                char join; /* Channel was 'j'-oined. */
        } chl[MaxChans];
        
       +static struct User {
       +        char nick[64];
       +        uint32_t channels; /* Needs to match MaxChans */
       +        char inuse;
       +} usrs[MaxKnownUsers];
       +
        static int ssl;
        static struct {
                int fd;
       @@ -84,6 +91,8 @@ static void tdrawbar(void);
        static void tredraw(void);
        static void treset(void);
        
       +static void usrchandrop(int);
       +
        static void
        panic(const char *m)
        {
       @@ -326,6 +335,7 @@ chdel(char *name)
        
                if (!(n = chfind(name)))
                        return 0;
       +        usrchandrop(n);
                nch--;
                free(chl[n].buf);
                memmove(&chl[n], &chl[n + 1], (nch - n) * sizeof(struct Chan));
       @@ -334,6 +344,81 @@ chdel(char *name)
                return 1;
        }
        
       +static void
       +usrchandrop(int chan)
       +{
       +        for (size_t i = 0; i < MaxKnownUsers; i++)
       +                if (usrs[i].channels == (1 << chan)) {
       +                        usrs[i].channels = 0;
       +                        usrs[i].inuse = 0;
       +                        bzero(usrs[i].nick, 64);
       +                }
       +}
       +
       +static size_t
       +usrfind(char *nick)
       +{
       +        size_t i = 0;
       +        for (i = MaxKnownUsers - 1; i > 0; i--){
       +                if (usrs[i].inuse && !strcmp(nick, usrs[i].nick))
       +                        break;
       +        }
       +        return i;
       +}
       +
       +static void
       +usradd(char* nick, int chan)
       +{
       +        size_t i;
       +        i = usrfind(nick);
       +        if (!i) {
       +                for (i = MaxKnownUsers - 1; i > 0; i--)
       +                        if (!usrs[i].inuse)
       +                                break;
       +        }
       +        if (!i)
       +                panic("Too many users!");
       +        strlcpy(usrs[i].nick, nick, 64);
       +        usrs[i].channels |= 1 << chan;
       +        usrs[i].inuse = 1;
       +}
       +
       +static void
       +usrdel(char* nick, int chan)
       +{
       +        size_t h;
       +
       +        h = usrfind(nick);
       +        if (!h)
       +                return;
       +        if (!chan)
       +                usrs[h].channels = 0;
       +
       +        usrs[h].channels &= ~(1 << chan);
       +
       +        if (!usrs[h].channels) {
       +                        usrs[h].inuse = 0;
       +                        bzero(usrs[h].nick, 64);
       +        }
       +}
       +
       +static void
       +usrchange(char* old, char* new)
       +{
       +        size_t h;
       +
       +        h = usrfind(old);
       +        if(!h)
       +                panic("Missed a user!");
       +        strlcpy(usrs[h].nick, new, 64);
       +}
       +
       +static uint32_t
       +usrchans(char* nick)
       +{
       +        return usrs[usrfind(nick)].channels;
       +}
       +
        static char *
        pushl(char *p, char *e)
        {
       @@ -461,7 +546,7 @@ scmd(char *usr, char *cmd, char *par, char *data)
                                tdrawbar();
                        }
                } else if (!strcmp(cmd, "NICK")) {
       -                if (!data || !pm)
       +                if (!data)
                                return;
                        if (!strcmp(usr, nick)){
                                for (int c=0; c < nch; c++){
       @@ -469,21 +554,33 @@ scmd(char *usr, char *cmd, char *par, char *data)
                                }
                                strlcpy(nick, data, sizeof(nick));
                        } else {
       -                        pushf(chfind(pm), "%s - is now known as %s", usr, data);
       +                        pushf(chfind(data), "%s - is now known as %s", usr, data);
       +                        usrchange(usr, data);
                        }
                        tredraw();
                        return;
                } else if (!strcmp(cmd, "PING")) {
                        sndf("PONG :%s", data ? data : "(null)");
                } else if (!strcmp(cmd, "PART")) {
       +                int ch = 0;
                        if (!pm)
                                return;
       -                pushf(chfind(pm), "-!- %s has left %s", usr, pm);
       +                ch = chfind(pm);
       +                pushf(ch, "-!- %s has left %s", usr, pm);
       +                usrdel(usr, ch);
                } else if (!strcmp(cmd, "JOIN")) { /* some servers pass the channel as data :#channel */
       -                if (pm)
       -                        pushf(chfind(pm), "-!- %s has joined %s", usr, pm);
       -                else if (data)
       -                        pushf(chfind(data), "-!- %s has joined %s", usr, data);
       +                int ch;
       +                char *chan;
       +                if (pm) {
       +                        ch = chfind(pm);
       +                        chan = pm;
       +                } else if (data) {
       +                        ch = chfind(data);
       +                        chan = data;
       +                }
       +
       +                pushf(ch, "-!- %s has joined %s", usr, chan);
       +                usradd(usr, ch);
                        return;
                } else if (!strcmp(cmd, "470")) { /* Channel forwarding. */
                        char *ch = strtok(0, " "), *fch = strtok(0, " ");
       @@ -500,6 +597,23 @@ scmd(char *usr, char *cmd, char *par, char *data)
                                return;
                        pushf(chfind(chan), "Topic for %s: %s", chan, data);
                        tredraw();
       +        } else if (!strcmp(cmd, "353")) { /* RPL_NAMREPLY */
       +                pushf(0, "Names %s", data ? data : "");
       +                if ((pm = strtok(0, " ")) && (!strcmp(pm, "=") || !strcmp(pm, "*") || !strcmp(pm, "@"))) {
       +                        char *n;
       +                        char *chan = strtok(0, " ");
       +                        int c = chfind(chan);
       +                        if (!chan || !data || !c)
       +                                return;
       +                        n = strtok(data, " ");
       +                        if (!n)
       +                                return;
       +                        do {
       +                                if (n[0] == '@' || n[0] == '+')
       +                                        n+=1;
       +                                usradd(n, c);
       +                        } while ((n = strtok(0, " ")));
       +                }
                } else if (!strcmp(cmd, "471") || !strcmp(cmd, "473")
                           || !strcmp(cmd, "474") || !strcmp(cmd, "475")) { /* Join error. */
                        if ((pm = strtok(0, " "))) {
       @@ -513,7 +627,19 @@ scmd(char *usr, char *cmd, char *par, char *data)
                                return;
                        pushf(0, "-!- Cannot change to nick %s: %s", pm, data);
                        tredraw();
       -        } else if (!strcmp(cmd, "QUIT")) { /* Commands we don't care about. */
       +        } else if (!strcmp(cmd, "QUIT")) {
       +                char *msg = "";
       +                if (!usr)
       +                        return;
       +                uint64_t chans = usrchans(usr);
       +                usrdel(usr, 0);
       +                if (data)
       +                        msg = data;
       +                for (int c = 0; c < MaxChans; c++) {
       +                        if (1<<c & chans)
       +                                pushf(c, "-!- %s has left ('%s').", usr, msg);
       +                }
       +                tredraw();
                        return;
                } else if (!strcmp(cmd, "NOTICE") || !strcmp(cmd, "375")
                       || !strcmp(cmd, "372") || !strcmp(cmd, "376")) {
       @@ -894,6 +1020,7 @@ main(int argc, char *argv[])
                        strcpy(nick, user);
                if (!nick[0])
                        goto usage;
       +        bzero(usrs, MaxKnownUsers * sizeof(*usrs));
                tinit();
                err = dial(server, port);
                if (err)