23 Commits
1.0 ... 1.2

Author SHA1 Message Date
fe525b3209 prepared a new release 2013-05-05 17:42:52 +02:00
a28f8b49c8 a few corrections to manpage 2012-04-20 00:15:32 +01:00
bade9ccb6c reversed the -u switch 2012-02-05 16:47:38 +01:00
2831ca36c3 applied Quentin Carbonneaux's user patch 2012-01-22 18:27:17 +01:00
3b5d848cd4 applied Jeroen's manpage patch, thanks! 2011-04-08 12:51:16 +00:00
efb5435694 applied Dimitris patches, thanks 2011-03-06 07:37:21 +00:00
fdaf2adbc9 fixed issue reported by serge on irc, util.c was excluded in dist target 2010-08-06 09:52:12 +01:00
a4ba8e3b08 Added tag 1.1 for changeset d6140e3685b8 2010-05-25 12:03:25 -04:00
6af1b812ae cleanup 2010-05-14 18:01:20 +01:00
75d42255f2 applied anonymous cleanup patch, thanks anonymous! 2010-04-17 11:12:54 +01:00
dad75aa6ba removed debug line 2010-03-23 18:00:37 +00:00
d78d10d1a1 bugfix 2010-03-22 13:29:33 +00:00
1a4256ef71 merged kris' changes into mainstream sic 2009-11-28 12:16:35 +00:00
9c7027fcbf applied clamiax' patch for null messages 2009-10-30 09:34:59 +00:00
7f1961d4c6 Update sic/util.c to match sic.c tip. 2009-09-23 12:52:13 -04:00
2b85380411 Update kris/util.c strlcpy 2009-09-23 11:26:48 -04:00
14e430ac5b 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 2009-09-23 14:32:20 +01:00
261dc71e58 applied portions of Mark Edgars patch 2009-09-23 14:21:03 +01:00
4a4f924fee applied Martin Kopta's splint patch 2009-08-07 08:37:09 +01:00
b3ed5f414f thanks to Matthias-Christian Ott for this hint 2009-02-21 19:14:13 +00:00
ca6ba9a64a applied Jeroen Schot's patch 2009-02-14 11:53:47 +00:00
f3827eec2a applied Matthias-Christian Ott's remark about trailing newlines 2009-02-14 11:42:45 +00:00
8b2eeee541 Added tag 1.0 for changeset b8794f3ed15e 2008-07-29 19:22:08 +01:00
7 changed files with 246 additions and 198 deletions

View File

@ -7,3 +7,5 @@ d7923d9e717c1c6f1ed3b17ec90bfdd7e7bfcca0 0.6
643a6e8b8634b70d2459637fcfff6eca776fc919 0.7
07fb3efaa2e9ed18c6c16f0ddd8576cb66fec9c6 0.8
96eb1bfede5b72fcee3f515d3113d814f7e87108 0.9
b8794f3ed15e34b24ff9fb11c93a4405d0f91433 1.0
d6140e3685b89c609a731471eba6787f7869aa59 1.1

View File

@ -1,6 +1,8 @@
MIT/X Consortium License
© 2005-2008 Anselm R Garbe <garbeam at gmail dot com>
© 2005-2013 Anselm R Garbe <anselm@garbe.us>
© 2008-2009 Jeroen Schot <schot@a-eskwadraat.nl>
© 2007-2009 Kris Maglione <maglione.k@gmail.com>
© 2005 Nico Golde <nico at ngolde dot de>
Permission is hereby granted, free of charge, to any person obtaining a

View File

@ -17,7 +17,7 @@ options:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: config.mk
${OBJ}: config.mk util.c
sic: ${OBJ}
@echo CC -o $@
@ -30,7 +30,7 @@ clean:
dist: clean
@echo creating dist tarball
@mkdir -p sic-${VERSION}
@cp -R LICENSE Makefile README config.mk sic.1 ${SRC} sic-${VERSION}
@cp -R LICENSE Makefile README config.mk sic.1 sic.c util.c sic-${VERSION}
@tar -cf sic-${VERSION}.tar sic-${VERSION}
@gzip sic-${VERSION}.tar
@rm -rf sic-${VERSION}

View File

@ -1,5 +1,5 @@
# sic version
VERSION = 1.0
VERSION = 1.2
# Customize below to fit your system

51
sic.1
View File

@ -3,45 +3,52 @@
sic \- simple irc client
.SH SYNOPSIS
.B sic
.RB [ \-h " <host>"]
.RB [ \-p " <port>"]
.RB [ \-n " <nick>"]
.RB [ \-k " <keyword>"]
.RB [ \-h
.IR host ]
.RB [ \-p
.IR port ]
.RB [ \-n
.IR nick ]
.RB [ \-k
.IR pass ]
.RB [ \-v ]
.SH DESCRIPTION
.B sic
is an extremly fast, small and simple irc client. It reads commands from
standard input and prints all server output to standard output. It multiplexes
also all channel traffic into one output, that you don't have to switch
different channel buffers, that's actually a feature.
is an extremely fast, small and simple irc client. It reads commands from
standard input and prints all server output to standard output. It also
multiplexes all channel traffic into one output. That way you don't have to
switch different channel buffers. So that's actually a feature.
.SH OPTIONS
.TP
.B \-h <host>
.BI \-h " host"
Overrides the default host (irc.oftc.net)
.TP
.B \-p <port>
.BI \-p " port"
Overrides the default port (6667)
.TP
.B \-n <nickname>
.BI \-n " nick"
Override the default nick ($USER)
.TP
.B \-k <keyword>
Specifies the keyword to authenticate your nick on the host
.BI \-k " pass"
Specifies the PASS connection password
.TP
.BI \-v
Prints version information to standard output, then exits.
.B \-v
Prints version information to stderr, then exits
.SH COMMANDS
.TP
.B :j #channel
.BI :j " #channel"
Join a channel
.TP
.B :l #channel
.BI :l " #channel"
Leave a channel
.TP
.B :m #channel/user msg
Write a message to #channel/user
.BI :m " #channel/user message"
Send a message to channel or user
.TP
.B :s #channel/user
Set default channel/user
.BI :s " #channel/user"
Set default channel or user
.TP
Everything which is not a command is simply send the server.
.BI : command
Any line beginning with
.B :
is sent as a command

307
sic.c
View File

@ -1,242 +1,205 @@
/* © 2005-2008 Anselm R Garbe <garbeam at gmail dot com>
* © 2005 Nico Golde <nico at ngolde dot de>
* See LICENSE file for license details. */
/* See LICENSE file for license details. */
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#define PINGTIMEOUT 300
#define MAXMSG 4096
static void die(const char *errstr, ...);
static void printl(char *channel, char *msg);
static void privmsg(char *channel, char *msg);
static void parsein(char *msg);
static void parsesrv(char *msg);
static int readl(int fd, unsigned int len, char *buf);
static char *host = "irc.oftc.net";
static unsigned short port = 6667;
static char *password = NULL;
static char *port = "6667";
static char *password;
static char nick[32];
static char bufin[MAXMSG], bufout[MAXMSG];
static char bufin[4096];
static char bufout[4096];
static char channel[256];
static int srv;
static time_t trespond;
static FILE *srv;
void
die(const char *errstr, ...) {
#include "util.c"
static void
pout(char *channel, char *fmt, ...) {
static char timestr[18];
time_t t;
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_start(ap, fmt);
vsnprintf(bufout, sizeof bufout, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
void
printl(char *channel, char *msg) {
static char timestr[18];
time_t t = time(0);
t = time(NULL);
strftime(timestr, sizeof timestr, "%D %R", localtime(&t));
fprintf(stdout, "%-12.12s: %s %s\n", channel, timestr, msg);
fprintf(stdout, "%-12s: %s %s\n", channel, timestr, bufout);
}
void
static void
sout(char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(bufout, sizeof bufout, fmt, ap);
va_end(ap);
fprintf(srv, "%s\r\n", bufout);
}
static void
privmsg(char *channel, char *msg) {
if(channel[0] == 0)
if(channel[0] == '\0') {
pout("", "No channel to send to");
return;
snprintf(bufout, sizeof bufout, "<%s> %s", nick, msg);
printl(channel, bufout);
snprintf(bufout, sizeof bufout, "PRIVMSG %s :%s\r\n", channel, msg);
write(srv, bufout, strlen(bufout));
}
pout(channel, "<%s> %s", nick, msg);
sout("PRIVMSG %s :%s", channel, msg);
}
void
parsein(char *msg) {
char *p;
static void
parsein(char *s) {
char c, *p;
if(msg[0] == 0)
if(s[0] == '\0')
return;
if(msg[0] != ':') {
privmsg(channel, msg);
skip(s, '\n');
if(s[0] != ':') {
privmsg(channel, s);
return;
}
if(!strncmp(msg + 1, "j ", 2) && (msg[3] == '#'))
snprintf(bufout, sizeof bufout, "JOIN %s\r\n", msg + 3);
else if(!strncmp(msg + 1, "l ", 2))
snprintf(bufout, sizeof bufout, "PART %s :sic - 250 LOC are too much!\r\n", msg + 3);
else if(!strncmp(msg + 1, "m ", 2)) {
if((p = strchr(msg + 3, ' ')))
*(p++) = 0;
privmsg(msg + 3, p);
return;
c = *++s;
if(c != '\0' && isspace(s[1])) {
p = s + 2;
switch(c) {
case 'j':
sout("JOIN %s", p);
if(channel[0] == '\0')
strlcpy(channel, p, sizeof channel);
return;
case 'l':
s = eat(p, isspace, 1);
p = eat(s, isspace, 0);
if(!*s)
s = channel;
if(*p)
*p++ = '\0';
if(!*p)
p = "sic - 250 LOC are too much!";
sout("PART %s :%s", s, p);
return;
case 'm':
s = eat(p, isspace, 1);
p = eat(s, isspace, 0);
if(*p)
*p++ = '\0';
privmsg(s, p);
return;
case 's':
strlcpy(channel, p, sizeof channel);
return;
}
}
else if(!strncmp(msg + 1, "s ", 2)) {
strncpy(channel, msg + 3, sizeof channel);
return;
}
else
snprintf(bufout, sizeof bufout, "%s\r\n", msg + 1);
write(srv, bufout, strlen(bufout));
sout("%s", s);
}
void
parsesrv(char *msg) {
char *chan, *cmd, *p, *txt, *usr;
static void
parsesrv(char *cmd) {
char *usr, *par, *txt;
txt = NULL;
usr = host;
if(!msg || !(*msg))
if(!cmd || !*cmd)
return;
if(msg[0] != ':')
cmd = msg;
else {
if(!(p = strchr(msg, ' ')))
if(cmd[0] == ':') {
usr = cmd + 1;
cmd = skip(usr, ' ');
if(cmd[0] == '\0')
return;
*p = 0;
usr = msg + 1;
cmd = ++p;
if((p = strchr(usr, '!')))
*p = 0;
skip(usr, '!');
}
for(p = cmd; *p; p++) /* remove CRLFs */
if(*p == '\r' || *p == '\n')
*p = 0;
if((p = strchr(cmd, ':'))) {
*p = 0;
txt = ++p;
}
if(!strncmp("PONG", cmd, 4))
skip(cmd, '\r');
par = skip(cmd, ' ');
txt = skip(par, ':');
trim(par);
if(!strcmp("PONG", cmd))
return;
if(!strncmp("PRIVMSG", cmd, 7) && txt) {
if(!(p = strchr(cmd, ' ')))
return;
*p = 0;
chan = ++p;
for(; *p && *p != ' '; p++);
*p = 0;
snprintf(bufout, sizeof bufout, "<%s> %s", usr, txt);
printl(chan, bufout);
}
else if(!strncmp("PING", cmd, 4) && txt) {
snprintf(bufout, sizeof bufout, "PONG %s\r\n", txt);
write(srv, bufout, strlen(bufout));
}
if(!strcmp("PRIVMSG", cmd))
pout(par, "<%s> %s", usr, txt);
else if(!strcmp("PING", cmd))
sout("PONG %s", txt);
else {
snprintf(bufout, sizeof bufout, ">< %s: %s", cmd, txt ? txt : "");
printl(usr, bufout);
if(!strncmp("NICK", cmd, 4) && !strncmp(usr, nick, sizeof nick) && txt)
strncpy(nick, txt, sizeof nick);
pout(usr, ">< %s (%s): %s", cmd, par, txt);
if(!strcmp("NICK", cmd) && !strcmp(usr, nick))
strlcpy(nick, txt, sizeof nick);
}
}
int
readl(int fd, unsigned int len, char *buf) {
unsigned int i = 0;
char c;
do {
if(read(fd, &c, sizeof(char)) != sizeof(char))
return -1;
buf[i++] = c;
}
while(c != '\n' && i < len);
buf[i - 1] = 0;
return 0;
}
int
main(int argc, char *argv[]) {
int i;
int i, c;
struct timeval tv;
struct hostent *hp;
static struct sockaddr_in addr; /* initially filled with 0's */
char ping[256];
const char *user = getenv("USER");
fd_set rd;
strncpy(nick, getenv("USER"), sizeof nick);
for(i = 1; i < argc; i++)
if(!strncmp(argv[i], "-h", 3)) {
strlcpy(nick, user ? user : "unknown", 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];
}
else if(!strncmp(argv[i], "-p", 3)) {
if(++i < argc) port = (unsigned short)atoi(argv[i]);
}
else if(!strncmp(argv[i], "-n", 3)) {
if(++i < argc) strncpy(nick, argv[i], sizeof nick);
}
else if(!strncmp(argv[i], "-k", 3)) {
break;
case 'p':
if(++i < argc) port = 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-2012 Kris Maglione, Anselm R. Garbe, Nico Golde\n");
default:
eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n");
}
else if(!strncmp(argv[i], "-v", 3))
die("sic-"VERSION", © 2005-2008 Anselm R Garbe, Nico Golde\n");
else
die("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n");
/* init */
if((srv = socket(AF_INET, SOCK_STREAM, 0)) < 0)
die("error: cannot connect host '%s'\n", host);
if(NULL == (hp = gethostbyname(host)))
die("error: cannot resolve hostname '%s'\n", host);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
if(connect(srv, (struct sockaddr *) &addr, sizeof(struct sockaddr_in))) {
close(srv);
die("error: cannot connect host '%s'\n", host);
}
/* init */
i = dial(host, port);
srv = fdopen(i, "r+");
/* login */
if(password)
snprintf(bufout, sizeof bufout,
"PASS %s\r\nNICK %s\r\nUSER %s localhost %s :%s\r\n",
password, nick, nick, host, nick);
else
snprintf(bufout, sizeof bufout, "NICK %s\r\nUSER %s localhost %s :%s\r\n",
nick, nick, host, nick);
write(srv, bufout, strlen(bufout));
snprintf(ping, sizeof ping, "PING %s\r\n", host);
channel[0] = 0;
setbuf(stdout, NULL); /* unbuffered stdout */
sout("PASS %s", password);
sout("NICK %s", nick);
sout("USER %s localhost %s :%s", nick, host, nick);
fflush(srv);
setbuf(stdout, NULL);
setbuf(srv, NULL);
for(;;) { /* main loop */
FD_ZERO(&rd);
FD_SET(0, &rd);
FD_SET(srv, &rd);
FD_SET(fileno(srv), &rd);
tv.tv_sec = 120;
tv.tv_usec = 0;
i = select(srv + 1, &rd, 0, 0, &tv);
i = select(fileno(srv) + 1, &rd, 0, 0, &tv);
if(i < 0) {
if(errno == EINTR)
continue;
die("error: error on select()");
eprint("sic: error on select():");
}
else if(i == 0) {
if(time(NULL) - trespond >= PINGTIMEOUT)
die("error: sic shutting down: parse timeout");
write(srv, ping, strlen(ping));
if(time(NULL) - trespond >= 300)
eprint("sic shutting down: parse timeout\n");
sout("PING %s", host);
continue;
}
if(FD_ISSET(srv, &rd)) {
if(readl(srv, sizeof bufin, bufin) == -1)
die("error: remote host closed connection");
if(FD_ISSET(fileno(srv), &rd)) {
if(fgets(bufin, sizeof bufin, srv) == NULL)
eprint("sic: remote host closed connection\n");
parsesrv(bufin);
trespond = time(NULL);
}
if(FD_ISSET(0, &rd)) {
if(readl(0, sizeof bufin, bufin) == -1)
die("error: broken pipe");
if(fgets(bufin, sizeof bufin, stdin) == NULL)
eprint("sic: broken pipe\n");
parsein(bufin);
}
}

74
util.c Normal file
View File

@ -0,0 +1,74 @@
/* See LICENSE file for license details. */
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
static void
eprint(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(bufout, sizeof bufout, fmt, ap);
va_end(ap);
fprintf(stderr, "%s", bufout);
if(fmt[0] && fmt[strlen(fmt) - 1] == ':')
fprintf(stderr, " %s\n", strerror(errno));
exit(1);
}
static int
dial(char *host, char *port) {
static struct addrinfo hints;
int srv;
struct addrinfo *res, *r;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if(getaddrinfo(host, port, &hints, &res) != 0)
eprint("error: cannot resolve hostname '%s':", host);
for(r = res; r; r = r->ai_next) {
if((srv = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) == -1)
continue;
if(connect(srv, r->ai_addr, r->ai_addrlen) == 0)
break;
close(srv);
}
freeaddrinfo(res);
if(!r)
eprint("error: cannot connect to host '%s'\n", host);
return srv;
}
#define strlcpy _strlcpy
static void
strlcpy(char *to, const char *from, int l) {
memccpy(to, from, '\0', l);
to[l-1] = '\0';
}
static char *
eat(char *s, int (*p)(int), int r) {
while(s != '\0' && p(*s) == r)
s++;
return s;
}
static char*
skip(char *s, char c) {
while(*s != c && *s != '\0')
s++;
if(*s != '\0')
*s++ = '\0';
return s;
}
static void
trim(char *s) {
char *e;
e = s + strlen(s) - 1;
while(isspace(*e) && e > s)
e--;
*(e + 1) = '\0';
}