sic

simple irc client
git clone git://git.suckless.org/sic
Log | Files | Refs | README | LICENSE

commit 14e430ac5b398e0c47f1d80f3c4f4b6386f545c2
parent 261dc71e58261c801e2be4db413e30b066ecf68f
Author: Anselm R Garbe <garbeam@gmail.com>
Date:   Wed, 23 Sep 2009 14:32:20 +0100

added kris' sic.c and util.c temporarily, will need some time to see what will and won't be integrated into mainstream sic.c
Diffstat:
kris/sic.c | 223+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
kris/util.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 296 insertions(+), 0 deletions(-)

diff --git a/kris/sic.c b/kris/sic.c @@ -0,0 +1,223 @@ +/* ? 2005-2007 Anselm R. Garbe <garbeam at gmail dot com> + * ? 2007 Kris Maglione <fbsdaemon@gmail.com> + * ? 2005 Nico Golde <nico at ngolde dot de> + * See LICENSE file for license details. + */ +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#define nil ((void*)0) +typedef unsigned short ushort; + +#define PINGTIMEOUT 300 + +static char* host = "irc.oftc.net"; +static ushort port = 6667; +static char* password; +static char nick[32]; + +static char bufin[4096]; +static char bufout[4096]; +static char channel[256]; +static time_t trespond; +static FILE *srv; + +#define va_buf(buf, fmt) {\ + va_list ap; \ + \ + va_start(ap, fmt); \ + vsnprintf(buf, sizeof buf, fmt, ap); \ + va_end(ap); \ +} + +#include "util.c" + +static void +pout(char *channel, char *fmt, ...) { + static char timestr[18]; + time_t t; + + va_buf(bufout, fmt); + + t = time(nil); + strftime(timestr, sizeof timestr, "%D %R", localtime(&t)); + fprintf(stdout, "%-12s: %s %s\n", channel, timestr, bufout); +} + +static void +sout(char *fmt, ...) { + va_buf(bufout, fmt); + fprintf(srv, "%s\r\n", bufout); +} + +static void +privmsg(char *channel, char *msg) { + if(channel[0] == '\0') { + pout("", "No channel to send to"); + return; + } + pout(channel, "<%s> %s", nick, msg); + sout("PRIVMSG %s :%s", channel, msg); +} + +static void +parsein(char *msg) { + char *p; + char c; + + if(msg[0] == '\0') + return; + msg = ctok(&msg, '\n'); + if(msg[0] != ':') { + privmsg(channel, msg); + return; + } + c = *++msg; + if(!c || !isspace(msg[1])) + sout("%s", msg); + else { + if(msg[1]) + msg += 2; + switch(c) { + case 'j': + sout("JOIN %s", msg); + if(channel[0] == '\0') + strlcpy(channel, msg, sizeof channel); + break; + case 'l': + p = tok(&msg); + if(!*p) + p = channel; + if(!*msg) + msg = "sic - 250 LOC are too much!"; + sout("PART %s :%s", p, msg); + break; + case 'm': + p = tok(&msg); + privmsg(p, msg); + break; + case 's': + strlcpy(channel, msg, sizeof channel); + break; + default: + sout("%c %s", c, msg); + break; + } + } +} + +static void +parsesrv(char *msg) { + char *cmd, *p, *usr, *txt; + + usr = host; + if(!msg || !*msg) + return; + if(msg[0] == ':') { + msg++; + p = tok(&msg); + if(!*msg) + return; + usr = ctok(&p, '!'); + } + txt = ctok(&msg, '\r'); + msg = ctok(&txt, ':'); + cmd = tok(&msg); + if(!strcmp("PONG", cmd)) + return; + if(!strcmp("PRIVMSG", cmd)) + pout(msg, "<%s> %s", usr, txt); + else if(!strcmp("PING", cmd)) + sout("PONG %s", txt); + else { + pout(usr, ">< %s: %s", cmd, txt); + if(!strcmp("NICK", cmd) && !strcmp(usr, nick)) + strlcpy(nick, txt, sizeof nick); + } +} + +int +main(int argc, char *argv[]) { + int i, c; + struct timeval tv; + fd_set rd; + + strlcpy(nick, getenv("USER"), sizeof nick); + for(i = 1; i < argc; i++) { + c = argv[i][1]; + if(argv[i][0] != '-' || argv[i][2]) + c = -1; + switch(c) { + case 'h': + if(++i < argc) host = argv[i]; + break; + case 'p': + if(++i < argc) port = atoi(argv[i]); + break; + case 'n': + if(++i < argc) strlcpy(nick, argv[i], sizeof nick); + break; + case 'k': + if(++i < argc) password = argv[i]; + break; + case 'v': + eprint("sic-"VERSION", ? 2005-2007 Anselm R. Garbe, Nico Golde\n"); + default: + eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n"); + } + } + + /* init */ + i = dial(host, port); + srv = fdopen(i, "r+"); + + /* login */ + if(password) + sout("PASS %s", password); + sout("NICK %s", nick); + sout("USER %s localhost %s :%s", nick, host, nick); + fflush(srv); + + setbuf(stdout, nil); + setbuf(srv, nil); + + for(;;) { /* main loop */ + FD_ZERO(&rd); + FD_SET(0, &rd); + FD_SET(fileno(srv), &rd); + tv.tv_sec = 120; + tv.tv_usec = 0; + i = select(fileno(srv) + 1, &rd, 0, 0, &tv); + if(i < 0) { + if(errno == EINTR) + continue; + eprint("sic: error on select():"); + } + else if(i == 0) { + if(time(nil) - trespond >= PINGTIMEOUT) + eprint("sic shutting down: parse timeout\n"); + sout("PING %s", host); + continue; + } + if(FD_ISSET(fileno(srv), &rd)) { + if(fgets(bufin, sizeof bufin, srv) == nil) + eprint("sic: remote host closed connection\n"); + parsesrv(bufin); + trespond = time(nil); + } + if(FD_ISSET(0, &rd)) { + if(fgets(bufin, sizeof bufin, stdin) == nil) + eprint("sic: broken pipe\n"); + parsein(bufin); + } + } + return 0; +} + + diff --git a/kris/util.c b/kris/util.c @@ -0,0 +1,73 @@ +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> + +static void +eprint(const char *fmt, ...) { + + va_buf(bufout, fmt); + fprintf(stderr, "%s", bufout); + + if(fmt[0] && fmt[strlen(fmt)-1] == ':') + fprintf(stderr, " %s\n", strerror(errno)); + exit(1); +} + +static int +dial(char *host, int port) { + struct hostent *hp; + static struct sockaddr_in addr; + int i; + + if((i = socket(AF_INET, SOCK_STREAM, 0)) < 0) + eprint("sic: cannot connect host '%s':", host); + if(nil == (hp = gethostbyname(host))) + eprint("sic: cannot resolve hostname '%s': %s\n", host, hstrerror(h_errno)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); + if(connect(i, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) + eprint("sic: cannot connect host '%s':", host); + return i; +} + +#define strlcpy _strlcpy +static void +strlcpy(char *to, const char *from, int l) { + strncpy(to, from, l-1); + to[l-1] = '\0'; +} + +static void +eat(char **s, int (*p)(int), int r) { + char *q; + + for(q=*s; *q && p(*q) == r; q++) + ; + *s = q; +} + +static char* +tok(char **s) { + char *p; + + eat(s, isspace, 1); + p = *s; + eat(s, isspace, 0); + if(**s) *(*s)++ = '\0'; + return p; +} + +static char* +ctok(char **s, int c) { + char *p, *q; + + q = *s; + for(p=q; *p && *p != c; p++) + ; + if(*p) *p++ = '\0'; + *s = p; + return q; +} + +