Initial import in Git. - irc - Unnamed repository; edit this file 'description' to name the repository.
 (HTM) git clone git://vernunftzentrum.de/irc.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit 9853570e09cd34fc5fd6a4a0c03055e5012920ec
 (HTM) Author: Quentin Carbonneaux <qcarbonneaux@gmail.com>
       Date:   Sat, 10 Mar 2012 13:23:07 +0100
       
       Initial import in Git.
       
       Diffstat:
         .comfile                            |       1 +
         .gitignore                          |       3 +++
         irc.c                               |     520 +++++++++++++++++++++++++++++++
       
       3 files changed, 524 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/.comfile b/.comfile
       @@ -0,0 +1 @@
       +irc.c
 (DIR) diff --git a/.gitignore b/.gitignore
       @@ -0,0 +1,2 @@
       +irc
       +*.sw[po]
       +\ No newline at end of file
 (DIR) diff --git a/irc.c b/irc.c
       @@ -0,0 +1,520 @@
       +/*% cc -g -Wall -lncurses -o # %
       + */
       +#include <assert.h>
       +#include <limits.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdarg.h>
       +#include <string.h>
       +#include <time.h>
       +#include <errno.h>
       +
       +#include <curses.h>
       +#include <unistd.h>
       +#include <arpa/inet.h>
       +#include <sys/types.h>
       +#include <sys/socket.h>
       +#include <sys/select.h>
       +#include <netinet/in.h>
       +#include <netinet/tcp.h>
       +#include <netdb.h>
       +#include <locale.h>
       +
       +#define SCROLL 15
       +#define DATEFMT "%T"
       +#define PFMT "%-12s < %s"
       +
       +enum { ChanLen = 64, LineLen = 512, MaxChans = 16, BufSz = 2048, LogSz = 4096 };
       +
       +char nick[64];
       +char prefix[64];
       +int quit;
       +int sfd; /* Server file descriptor. */
       +struct {
       +        int x;
       +        int y;
       +        WINDOW *sw, *mw, *iw;
       +} scr; /* Screen relative data. */
       +int eof; /* EOF reached on server side. */
       +struct Chan {
       +        char name[ChanLen];
       +        char *buf, *eol;
       +        size_t n, sz; /* n is the scoll offset, sz is size of buf. */
       +} chl[MaxChans];
       +int nch, ch; /* Current number of channels, and current channel. */
       +char outb[BufSz], *outp=outb; /* Output buffer. */
       +
       +static void scmd(char *, char *, char *, char *);
       +static void tredraw(void);
       +static void treset(void);
       +
       +static void
       +panic(const char *m)
       +{
       +        treset();
       +        fprintf(stderr, "Panic: %s\n", m);
       +        exit(1);
       +}
       +
       +static void
       +sndf(const char *fmt, ...)
       +{
       +        va_list vl;
       +        size_t n, l=BufSz-(outp-outb);
       +
       +        if (l<2) return;
       +        va_start(vl, fmt);
       +        n=vsnprintf(outp, l-2, fmt, vl);
       +        va_end(vl);
       +        outp += n>l-2 ? l-2 : n;
       +        *outp++ = '\r';
       +        *outp++ = '\n';
       +}
       +
       +static int
       +srd(void)
       +{
       +        static char l[BufSz], *p=l;
       +        char *s, *usr, *cmd, *par, *data;
       +        int rd;
       +
       +        if (p-l>=BufSz) p=l; /* Input buffer overflow, there should something better to do. */
       +        rd=read(sfd, p, BufSz-(p-l));
       +        if (rd<0) {
       +                if (errno==EINTR) return 1;
       +                panic("IO error while reading.");
       +        }
       +        if (rd==0) return 0;
       +        p+=rd;
       +        for (;;) { /* Cycle on all received lines. */
       +                if (!(s=memchr(l, '\n', p-l)))
       +                        return 1;
       +                if (s>l && s[-1]=='\r')
       +                        s[-1]=0;
       +                *s++ = 0;
       +                if (*l==':') {
       +                        if (!(cmd=strchr(l, ' '))) goto lskip;
       +                                *cmd++ = 0;
       +                        usr = l+1;
       +                } else {
       +                        usr = 0;
       +                        cmd = l;
       +                }
       +                if (!(par=strchr(cmd, ' '))) goto lskip;
       +                *par++ = 0;
       +                if ((data=strchr(par, ':')))
       +                        *data++ = 0;
       +                scmd(usr, cmd, par, data);
       +        lskip:
       +                memmove(l, s, p-s);
       +                p-=s-l;
       +        }
       +}
       +
       +static int
       +dial(const char *host, short port)
       +{
       +        int f;
       +        struct sockaddr_in sin;
       +        struct addrinfo *ai, hai;
       +
       +        hai.ai_family = AF_INET;
       +        hai.ai_socktype = SOCK_STREAM;
       +        hai.ai_flags = hai.ai_protocol = 0;
       +        if (getaddrinfo(host, 0, &hai, &ai))
       +                panic("Cannot resolve host.");
       +        memcpy(&sin, ai->ai_addr, sizeof sin);
       +        sin.sin_port = htons(port);
       +        freeaddrinfo(ai);
       +        f = socket(AF_INET, SOCK_STREAM, 0);
       +        if (f<0)
       +                panic("Cannot create socket.");
       +        if (connect(f, (struct sockaddr *)&sin, sizeof sin)<0)
       +                panic("Cannot connect to host.");
       +        return f;
       +}
       +
       +static int
       +chadd(char *name)
       +{
       +        if (nch>=MaxChans || strlen(name)>=ChanLen)
       +                return -1;
       +        strcpy(chl[nch].name, name);
       +        chl[nch].sz=LogSz;
       +        chl[nch].buf=malloc(LogSz);
       +        if (!chl[nch].buf)
       +                panic("Out of memory.");
       +        chl[nch].eol=chl[nch].buf;
       +        chl[nch].n=0;
       +        ch=nch;
       +        return nch++;
       +}
       +
       +static inline int
       +chfind(char *name)
       +{
       +        int i;
       +
       +        assert(name);
       +        for (i=nch-1; i>0; i--)
       +                if (!strcmp(chl[i].name, name))
       +                        break;
       +        return i;
       +}
       +
       +static int
       +chdel(char *name)
       +{
       +        int n;
       +
       +        if (!(n=chfind(name))) return 0;
       +        nch--;
       +        free(chl[n].buf);
       +        memmove(&chl[n], &chl[n+1], (nch-n)*sizeof(struct Chan));
       +        ch=nch-1;
       +        return 1;
       +}
       +
       +static void
       +pushm(int cn, char *msg)
       +{
       +        struct Chan * const c=&chl[cn];
       +        size_t blen=c->eol-c->buf, l=strlen(msg);
       +
       +        if (blen+l>=c->sz) {
       +                do
       +                        c->sz *= 2;
       +                while (blen+l>=c->sz);
       +                c->buf=realloc(c->buf, c->sz);
       +                if (!c->buf) panic("Out of memory.");
       +                c->eol = c->buf+blen;
       +        }
       +        strcpy(c->eol, msg);
       +        c->eol+=l;
       +        if (cn==ch && c->n==0) /* Redraw if the current channel was modified. */
       +                tredraw();
       +}
       +
       +static void
       +pushf(int cn, const char *fmt, ...)
       +{
       +        //struct Chan *const c=&chl[cn];
       +        char lb[512];
       +        size_t n;
       +        time_t t;
       +        struct tm *tm;
       +        va_list vl;
       +
       +        t=time(0);
       +        tm=localtime(&t);
       +        if (!tm)
       +                panic("localtime failed.");
       +        n=strftime(lb, sizeof lb, DATEFMT, tm);
       +        lb[n]=' ';
       +        if (!n)
       +                panic("strftime failed.");
       +        va_start(vl, fmt);
       +        n+=vsnprintf(lb+n+1, sizeof lb-n-3, fmt, vl);
       +        va_end(vl);
       +        strcat(lb, "\n");
       +        pushm(cn, lb);
       +}
       +
       +static void
       +scmd(char *usr, char *cmd, char *par, char *data)
       +{
       +        int s;
       +        char *pm=strtok(par, " ");
       +
       +        if (!usr) usr="?";
       +        else {
       +                char *bang=strchr(usr, '!');
       +                if (bang)
       +                        *bang=0;
       +        }
       +        if (!strcmp(cmd, "PRIVMSG")) {
       +                if (!pm || !data) return;
       +                pushf(chfind(pm), PFMT, usr, data);
       +        } else if (!strcmp(cmd, "PING")) {
       +                sndf("PONG :%s", data?data:"(null)");
       +        } else if (!strcmp(cmd, "PART")) {
       +                if (!pm) return;
       +                pushf(chfind(pm), "-!- %s has left %s", usr, pm);
       +        } else if (!strcmp(cmd, "JOIN")) {
       +                if (!pm) return;
       +                pushf(chfind(pm), "-!- %s has joined %s", usr, pm);
       +        } else if (!strcmp(cmd, "470")) { /* Channel forwarding. */
       +                char *ch=strtok(0, " "), *fch=strtok(0, " ");
       +                if (!ch || !fch || !(s=chfind(ch))) return;
       +                chl[s].name[0] = 0;
       +                strncat(chl[s].name, fch, ChanLen-1);
       +        } else if (!strcmp(cmd, "471") || !strcmp(cmd, "473")
       +               || !strcmp(cmd, "474") || !strcmp(cmd, "475")) { /* Join error. */
       +                if ((pm=strtok(0, " "))) {
       +                        chdel(pm);
       +                        pushf(0, "-!- Cannot join channel %s (%s)", pm, cmd);
       +                        tredraw();
       +                }
       +        } else if (!strcmp(cmd, "QUIT")) { /* Commands we don't care about. */
       +                return;
       +        } else
       +                pushf(0, "%s - %s %s", cmd, par, data?data:"(null)");
       +}
       +
       +static void
       +uparse(char *m)
       +{
       +        char *p=m;
       +
       +        if (p[1]!=' ' && p[1]!=0) {
       +        pmsg:
       +                if (ch!=0) {
       +                        m+=strspn(m, " ");
       +                        if (!*m) return;
       +                        pushf(ch, PFMT, nick, m);
       +                        sndf("PRIVMSG %s :%s", chl[ch].name, m);
       +                }
       +                return;
       +        }
       +        switch (*p) {
       +        case 'j': /* Join channels. */
       +                p+=1+(p[1]==' ');
       +                p=strtok(p, " ");
       +                while (p) {
       +                        if (chadd(p)<0) break;
       +                        sndf("JOIN %s", p);
       +                        p=strtok(0, " ");
       +                }
       +                tredraw();
       +                return;
       +        case 'l': /* Leave channels. */
       +                p+=1+(p[1]==' ');
       +                if (!*p) {
       +                        if (ch==0) return; /* Cannot leave server window. */
       +                        strcat(p, chl[ch].name);
       +                }
       +                p=strtok(p, " ");
       +                while (p) {
       +                        if (chdel(p))
       +                                sndf("PART %s", p);
       +                        p=strtok(0, " ");
       +                }
       +                tredraw();
       +                return;
       +        case 'm': /* Private message. */
       +                m=p+1+(p[1]==' ');
       +                if (!(p=strchr(m, ' '))) return;
       +                *p++ = 0;
       +                sndf("PRIVMSG %s :%s", m, p);
       +                return;
       +        case 'r': /* Send raw. */
       +                sndf("%s", m);
       +                return;
       +        case 'q': /* Quit. */
       +                quit=1;
       +                return;
       +        default: /* Send on current channel. */
       +                goto pmsg;
       +        }
       +}
       +
       +static void
       +tinit(void)
       +{
       +        setlocale(LC_ALL, "");
       +        initscr();
       +        raw();
       +        noecho();
       +        getmaxyx(stdscr, scr.y, scr.x);
       +        if (scr.y<4) panic("Screen too small.");
       +        if ((scr.sw=newwin(1, scr.x, 0, 0))==0
       +        || (scr.mw=newwin(scr.y-2, scr.x, 1, 0))==0
       +        || (scr.iw=newwin(1, scr.x, scr.y-1, 0))==0)
       +                panic("Cannot create windows.");
       +        keypad(scr.iw, 1);
       +        if (has_colors()==TRUE) {
       +                start_color();
       +                init_pair(1, COLOR_WHITE, COLOR_BLUE);
       +                wbkgd(scr.sw, COLOR_PAIR(1));
       +        }
       +}
       +
       +static void
       +tredraw(void)
       +{
       +        struct Chan * const c=&chl[ch];
       +        char *q, *p;
       +        int llen=0, nl=0;
       +
       +        if (c->eol==c->buf) {
       +                wclear(scr.mw);
       +                wrefresh(scr.mw);
       +                return;
       +        }
       +        p=c->eol-1;
       +        if (c->n) {
       +                int i=c->n;
       +                for (; p>c->buf; p--)
       +                        if (*p=='\n' && !i--) break;
       +                if (p==c->buf) c->n-=i;
       +        }
       +        q=p;
       +        while (nl<scr.y-2) {
       +                llen=0;
       +                while (*q!='\n' && q>c->buf)
       +                        q--, llen++;
       +                nl += 1+llen/scr.x;
       +                if (q==c->buf) break;
       +                q--;
       +        }
       +        if (q!=c->buf) q+=2;
       +        for (llen=0; nl>scr.y-2; ) { /* Maybe we must split the top line. */
       +                if (q[llen]=='\n' || llen>=scr.x) {
       +                        q+=llen+(q[llen]=='\n');
       +                        llen=0;
       +                        nl--;
       +                } else llen++;
       +        }
       +        wclear(scr.mw);
       +        wmove(scr.mw, 0, 0);
       +        for (; q<p; q++)
       +                waddch(scr.mw, *q);
       +        wrefresh(scr.mw);
       +}
       +
       +static void
       +tgetch(void)
       +{
       +        static char lb[BufSz];
       +        static size_t cu=0, len=0;
       +        size_t dirty=len+1, i;
       +        int c;
       +
       +        c=wgetch(scr.iw);
       +        switch (c) {
       +        case 0xe: ch=(ch+1)%nch; tredraw(); return;
       +        case 0x10: ch=(ch+nch-1)%nch; tredraw(); return;
       +        case KEY_PPAGE:
       +                chl[ch].n+=SCROLL;
       +                tredraw();
       +                return;
       +        case KEY_NPAGE:
       +                chl[ch].n-=SCROLL;
       +                if (chl[ch].n<0) chl[ch].n=0;
       +                tredraw();
       +                return;
       +        case 0x1: cu=0; break;
       +        case 0x5: cu=len; break;
       +        case 0x2:
       +        case KEY_LEFT: if (cu) cu--; break;
       +        case 0x6:
       +        case KEY_RIGHT: if (cu<len) cu++; break;
       +        case 0xb: dirty=len=cu; break;
       +        case 0x15:
       +                if (cu==0) return;
       +                len-=cu;
       +                memmove(lb, &lb[cu], len);
       +                dirty=cu=0;
       +                break;
       +        case KEY_BACKSPACE:
       +                if (cu==0) return;
       +                memmove(&lb[cu-1], &lb[cu], len-cu);
       +                dirty=--cu;
       +                len--;
       +                break;
       +        case '\n':
       +                if (len==BufSz) len--;
       +                lb[len]=0;
       +                uparse(lb);
       +                dirty=cu=len=0;
       +                break;
       +        case KEY_RESIZE:
       +                getmaxyx(stdscr, scr.y, scr.x);
       +                if (scr.y<3 || scr.x<10) panic("Screen too small.");
       +                tredraw();
       +                return;
       +        default:
       +                if (c>CHAR_MAX || len>=BufSz) return; /* Skip other curses codes. */
       +                memmove(&lb[cu+1], &lb[cu], len-cu);
       +                dirty=cu;
       +                len++;
       +                lb[cu++]=c;
       +                break;
       +        }
       +        /* TODO, add a cleverer printer to deal with long lines. */
       +        if (dirty<=len) {
       +                wmove(scr.iw, 0, strlen(nick)+2+dirty);
       +                wclrtoeol(scr.iw);
       +                for (i=dirty; i<len; i++)
       +                        waddch(scr.iw, lb[i]);
       +        }
       +        wmove(scr.iw, 0, strlen(nick)+2+cu);
       +}
       +
       +static void
       +treset(void)
       +{
       +        if (scr.mw) delwin(scr.mw);
       +        if (scr.sw) delwin(scr.sw);
       +        if (scr.iw) delwin(scr.iw);
       +        endwin();
       +}
       +
       +int
       +main(void)
       +{
       +        const char *user = getenv("USER");
       +
       +        if (!user) user="Unknown";
       +        tinit();
       +        chadd("*server*");
       +        waddstr(scr.sw, "Welcome in irc.");
       +        wrefresh(scr.sw);
       +        strcpy(nick, "_mpu");
       +        waddstr(scr.iw, "_mpu< ");
       +        sfd = dial("chat.freenode.org", 6667);
       +        sndf("NICK %s", nick);
       +        sndf("USER brebi 8 * :%s", user);
       +        sndf("MODE %s +i", nick);
       +        while (!quit) {
       +                fd_set rfs, wfs;
       +                int ret;
       +                
       +                FD_ZERO(&wfs);
       +                FD_ZERO(&rfs);
       +                FD_SET(0, &rfs);
       +                FD_SET(sfd, &rfs);
       +                if (outp!=outb)
       +                        FD_SET(sfd, &wfs);
       +                ret=select(sfd+1, &rfs, &wfs, 0, 0);
       +                if (ret<0) {
       +                        if (errno==EINTR) continue;
       +                        panic("Select failed.");
       +                }
       +                if (FD_ISSET(sfd, &rfs)) {
       +                        if (!srd())
       +                                quit=1;
       +                }
       +                if (FD_ISSET(sfd, &wfs)) {
       +                        int wr;
       +                        
       +                        wr=write(sfd, outb, outp-outb);
       +                        if (wr<0) {
       +                                if (errno==EINTR) continue;
       +                                panic("Write error.");
       +                        }
       +                        if (wr==0) continue;
       +                        outp-=wr;
       +                        memmove(outb, outb+wr, outp-outb);
       +                }
       +                if (FD_ISSET(0, &rfs)) {
       +                        tgetch();
       +                        wrefresh(scr.iw);
       +                }
       +        }
       +        close(sfd);
       +        while (nch--)
       +                free(chl[nch].buf);
       +        treset();
       +        exit(0);
       +}