Introduction
Introduction Statistics Contact Development Disclaimer Help
Initial import in Git. - irc - Unnamed repository; edit this file 'description'…
git clone git://vernunftzentrum.de/irc.git
Log
Files
Refs
README
---
commit 9853570e09cd34fc5fd6a4a0c03055e5012920ec
Author: Quentin Carbonneaux <[email protected]>
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(-)
---
diff --git a/.comfile b/.comfile
@@ -0,0 +1 @@
+irc.c
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+irc
+*.sw[po]
+\ No newline at end of file
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 …
+ 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 cod…
+ 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);
+}
You are viewing proxied material from vernunftzentrum.de. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.