40 Commits
0.3 ... 0.9

Author SHA1 Message Date
82946e102d setting the topic must not be supported, its a trivial IRC command (*and rarely used btw*) 2007-02-13 16:14:52 +01:00
7ef7529687 I believe I have fixed the issue, why sic didn't handled PINGs correctly 2007-02-13 16:06:28 +01:00
bcda402396 fixed sic man page 2007-02-09 17:07:29 +01:00
1160be7ffa removed fullname, added support for tracking NICK changes 2007-02-09 16:16:06 +01:00
75f0af7c42 added eprint() 2007-02-09 16:04:49 +01:00
81238e16fe consistency patch 2007-02-09 15:40:58 +01:00
60fc462aa9 changing the special command operator 2007-02-09 15:18:08 +01:00
9b093ac5c6 removed debug output 2007-02-09 15:04:57 +01:00
264f8e9422 changed output 2007-02-09 15:02:56 +01:00
fe1e3560be small fix 2007-02-09 14:42:40 +01:00
1edd94af86 some changes 2007-02-09 14:40:11 +01:00
a3abf3b3ef Don't use sic's hg tip, it is broken atm... 2007-02-08 20:42:29 +01:00
e3cbbe71c2 adding debug support 2007-02-08 15:32:51 +01:00
93842a5fe0 fixed part message 2007-02-08 15:10:47 +01:00
1f1228547c small fix 2007-02-08 15:10:10 +01:00
ea71b6e88e small fix, don't write message to server if channel is not given 2007-02-08 15:03:42 +01:00
cb68ce9e2c some more simplifications 2007-02-08 14:51:04 +01:00
5569a14e8e testing some simplifications 2007-02-08 14:27:39 +01:00
2e945e48fb small changes, bigger simplifications are on its way 2007-02-06 16:42:17 +01:00
a63ccb4600 Added tag 0.8 for changeset 07fb3efaa2e9 2007-02-01 08:22:31 +01:00
0a903a0c79 using Tcmd successors more carefully 2007-01-31 11:01:34 +01:00
8edb0e23f4 Added tag 0.7 for changeset 643a6e8b8634 2007-01-11 11:44:41 +01:00
44e0417e14 applied some segfault patches of Adrien Krunch Kunysz (thanks to Adrien!) 2007-01-11 11:35:02 +01:00
148029905b V7 2006-12-14 16:52:12 +01:00
f8d2e7f236 fixed sizeof stuff 2006-11-26 15:53:33 +01:00
dc15c0d639 Added tag 0.6 for changeset d7923d9e717c1c6f1ed3b17ec90bfdd7e7bfcca0 2006-11-09 17:38:01 +01:00
73e181ce5b fixed a small grammar typo 2006-11-09 17:37:57 +01:00
9197fee8a9 new time info 2006-11-03 14:32:05 +01:00
45ff5ccf31 Added tag 0.5 for changeset 70d49a37b35695f2f771bddaf309f05ea60af8bc 2006-10-26 12:16:42 +02:00
fe306465d0 same problem in sic as in dmenu 2006-10-23 10:56:06 +02:00
1f3f048ab7 Added tag 0.4 for changeset a3549fb4c72ff0edb816c8c29be7ff289db5b003 2006-10-13 11:10:41 +02:00
3160fd2bed hotfix 2006-10-12 08:03:37 +02:00
560111d4ba hotfix 2006-10-11 18:23:03 +02:00
48fc6f1b10 removed useless crap 2006-10-09 18:01:26 +02:00
4120182a49 changed version 2006-10-09 07:28:43 +02:00
516c1cafaf fixed arg processing 2006-10-09 07:26:24 +02:00
3a24553892 code polishing, removed useless crap 2006-10-09 07:20:07 +02:00
83e05870ce small change to man page 2006-09-26 17:52:37 +02:00
089bad0eb0 updated README 2006-09-26 14:32:24 +02:00
1ad6394622 Added tag 0.3 for changeset d77f00af559258679a0fad5d264685d663e6975a 2006-09-26 14:27:34 +02:00
5 changed files with 130 additions and 238 deletions

View File

@ -1,2 +1,8 @@
de32c537aaf66554894712563ffba8d9bc4c2714 0.1
56350a01f27753cfbdbb3dbc25f2a53dd4c2ac45 0.2
d77f00af559258679a0fad5d264685d663e6975a 0.3
a3549fb4c72ff0edb816c8c29be7ff289db5b003 0.4
70d49a37b35695f2f771bddaf309f05ea60af8bc 0.5
d7923d9e717c1c6f1ed3b17ec90bfdd7e7bfcca0 0.6
643a6e8b8634b70d2459637fcfff6eca776fc919 0.7
07fb3efaa2e9ed18c6c16f0ddd8576cb66fec9c6 0.8

10
README
View File

@ -1,11 +1,11 @@
sic - simple irc client
-----------------------
=======================
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.
Installation
------------
Edit config.mk to match your local setup. sic is installed into
@ -16,11 +16,7 @@ Afterwards enter the following command to build and install sic
$ make clean install
Running sic
-----------
Simply invoke the 'sic' command with the required arguments.
Configuration
-------------
No configuration is needed.

View File

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

32
sic.1
View File

@ -3,11 +3,10 @@
sic \- simple irc client
.SH SYNOPSIS
.B sic
.RB [ \-s <server> ]
.RB [ \-p <port> ]
.RB [ \-n <nick> ]
.RB [ \-k <keyword> ]
.RB [ \-f <fullname> ]
.RB [ \-h " <host>"]
.RB [ \-p " <port>"]
.RB [ \-n " <nick>"]
.RB [ \-k " <keyword>"]
.RB [ \-v ]
.SH DESCRIPTION
.B sic
@ -17,8 +16,8 @@ also all channel traffic into one output, that you don't have to switch
different channel buffers, that's actually a feature.
.SH OPTIONS
.TP
.B \-s <server>
Overrides the default server (irc.oftc.net)
.B \-h <host>
Overrides the default host (irc.oftc.net)
.TP
.B \-p <port>
Overrides the default port (6667)
@ -27,29 +26,22 @@ Overrides the default port (6667)
Override the default nick ($USER)
.TP
.B \-k <keyword>
Specifies the keyword to authenticate your nick on the server
.TP
.B \-f <fullname>
Specify the real name (default is $USER)
Specifies the keyword to authenticate your nick on the host
.TP
.BI \-v
Prints version information to standard output, then exits.
.SH COMMANDS
.TP
.B /j #channel
.B :j #channel
Join a channel
.TP
.B /l #channel
.B :l #channel
Leave a channel
.TP
.B /m #channel/user msg
.B :m #channel/user msg
Write a message to #channel/user
.TP
.B /s #channel/user
.B :s #channel/user
Set default channel/user
.TP
.B /t topic
Set the channel topic
.TP
Everything which is not a command will simply be posted into the channel or to
the server.
Everything which is not a command is simply send the server.

318
sic.c
View File

@ -1,12 +1,11 @@
/*
* (C)opyright MMV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
/* (C)opyright MMV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* (C)opyright MMV-MMVI Nico Golde <nico at ngolde dot de>
* See LICENSE file for license details.
*/
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -18,24 +17,31 @@
#define PINGTIMEOUT 300
#define MAXMSG 4096
enum { Tnick, Tuser, Tcmd, Tchan, Targ, Ttext, Tlast };
static char *server = "irc.oftc.net";
static char *host = "irc.oftc.net";
static unsigned short port = 6667;
static char *nick = NULL;
static char *fullname = NULL;
static char *password = NULL;
static char nick[32];
static char bufin[MAXMSG], bufout[MAXMSG];
static char channel[256];
static int srv;
static time_t trespond;
static void
eprint(const char *errstr, ...) {
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
static int
getline(int fd, unsigned int len, char *buf)
{
getline(int fd, unsigned int len, char *buf) {
unsigned int i = 0;
char c;
do {
if(read(fd, &c, sizeof(char)) != sizeof(char))
return -1;
@ -47,189 +53,105 @@ getline(int fd, unsigned int len, char *buf)
}
static void
pout(char *channel, char *msg)
{
pout(char *channel, char *msg) {
static char timestr[18];
time_t t = time(0);
strftime(timestr, sizeof(timestr), "%a %R", localtime(&t));
fprintf(stdout, "%-8.8s: %s %s\n", channel, timestr, msg);
strftime(timestr, sizeof timestr, "%D %R", localtime(&t));
fprintf(stdout, "%-12.12s: %s %s\n", channel, timestr, msg);
}
static void
privmsg(char *channel, char *msg)
{
snprintf(bufout, sizeof(bufout), "<%s> %s", nick, msg);
privmsg(char *channel, char *msg) {
if(channel[0] == 0)
return;
snprintf(bufout, sizeof bufout, "<%s> %s", nick, msg);
pout(channel, bufout);
snprintf(bufout, sizeof(bufout), "PRIVMSG %s :%s\r\n", channel, msg);
snprintf(bufout, sizeof bufout, "PRIVMSG %s :%s\r\n", channel, msg);
write(srv, bufout, strlen(bufout));
}
static void
parsein(char *msg)
{
parsein(char *msg) {
char *p;
if(msg[0] == 0)
return;
if(msg[0] != '/') {
if(msg[0] != ':') {
privmsg(channel, msg);
return;
}
if(!strncmp(msg + 1, "j ", 2) && (msg[3] == '#'))
snprintf(bufout, sizeof(bufout), "JOIN %s\r\n", &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\r\n", &msg[3]);
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], ' '))
if((p = strchr(msg + 3, ' ')))
*(p++) = 0;
privmsg(&msg[3], p);
privmsg(msg + 3, p);
return;
}
else if(!strncmp(msg + 1, "s ", 2)) {
strncpy(channel, &msg[3], sizeof(channel));
strncpy(channel, msg + 3, sizeof channel);
return;
}
else if(!strncmp(msg + 1, "t ", 2)) {
if(p = strchr(&msg[3], ' '))
*(p++) = 0;
snprintf(bufout, sizeof(bufout), "TOPIC %s :%s\r\n", &msg[3], p);
}
else
snprintf(bufout, sizeof(bufout), "%s\r\n", &msg[1]);
snprintf(bufout, sizeof bufout, "%s\r\n", msg + 1);
write(srv, bufout, strlen(bufout));
}
static unsigned int
tokenize(char **result, unsigned int reslen, char *str, char delim)
{
char *p, *n;
unsigned int i = 0;
if(!str)
return 0;
for(n = str; *n == delim; n++);
p = n;
for(i = 0; *n != 0;) {
if(i == reslen)
return i;
if(*n == delim) {
*n = 0;
if(strlen(p))
result[i++] = p;
p = ++n;
} else
n++;
}
if((i < reslen) && (p < n) && strlen(p))
result[i++] = p;
return i; /* number of tokens */
}
static void
parsesrv(char *msg)
{
char *argv[Tlast], *cmd, *p;
int i;
parsesrv(char *msg) {
char *chan, *cmd, *p, *txt, *usr;
txt = NULL;
usr = host;
if(!msg || !(*msg))
return;
for(i = 0; i < Tlast; i++)
argv[i] = NULL;
/*
<bufout> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <server> ]
<command> ::= <letter> { <letter> } | <number> <number> <number>
<SPACE> ::= ' ' { ' ' }
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
or NUL or CR or LF, the first of which may not be ':'>
<trailing> ::= <Any, possibly *empty*, sequence of octets not including NUL or CR or LF>
<crlf> ::= CR LF
*/
if(msg[0] == ':') { /* check prefix */
p = strchr(msg, ' ');
*p = 0;
for(++p; *p == ' '; p++);
cmd = p;
argv[Tnick] = &msg[1];
if((p = strchr(msg, '!'))) {
*p = 0;
argv[Tuser] = ++p;
}
} else
if(msg[0] != ':')
cmd = msg;
/* remove CRLFs */
for(p = cmd; p && *p != 0; p++)
else {
if(!(p = strchr(msg, ' ')))
return;
*p = 0;
usr = msg + 1;
cmd = ++p;
if((p = strchr(usr, '!')))
*p = 0;
}
for(p = cmd; *p; p++) /* remove CRLFs */
if(*p == '\r' || *p == '\n')
*p = 0;
if((p = strchr(cmd, ':'))) {
*p = 0;
argv[Ttext] = ++p;
txt = ++p;
}
tokenize(&argv[Tcmd], Tlast - Tcmd, cmd, ' ');
if(!strncmp("PONG", argv[Tcmd], 5)) {
if(!strncmp("PONG", cmd, 4))
return;
} else if(!strncmp("PING", argv[Tcmd], 5)) {
snprintf(bufout, sizeof(bufout), "PONG %s\r\n", argv[Ttext]);
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);
pout(chan, bufout);
}
else if(!strncmp("PING", cmd, 4) && txt) {
snprintf(bufout, sizeof bufout, "PONG %s\r\n", txt);
write(srv, bufout, strlen(bufout));
return;
} else if(!argv[Tnick] || !argv[Tuser]) { /* server command */
snprintf(bufout, sizeof(bufout), "%s", argv[Ttext] ? argv[Ttext] : "");
pout(server, bufout);
return;
} else if(!strncmp("ERROR", argv[Tcmd], 6))
snprintf(bufout, sizeof(bufout), "-!- error %s",
argv[Ttext] ? argv[Ttext] : "unknown");
else if(!strncmp("JOIN", argv[Tcmd], 5)) {
if(argv[Ttext]!=NULL){
p = strchr(argv[Ttext], ' ');
if(p)
*p = 0;
}
argv[Tchan] = argv[Ttext];
snprintf(bufout, sizeof(bufout), "-!- %s(%s) has joined %s",
argv[Tnick], argv[Tuser], argv[Ttext]);
} else if(!strncmp("PART", argv[Tcmd], 5)) {
snprintf(bufout, sizeof(bufout), "-!- %s(%s) has left %s",
argv[Tnick], argv[Tuser], argv[Tchan]);
} else if(!strncmp("MODE", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s changed mode/%s -> %s %s",
argv[Tnick], argv[Tcmd + 1],
argv[Tcmd + 2], argv[Tcmd + 3]);
else if(!strncmp("QUIT", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s(%s) has quit \"%s\"",
argv[Tnick], argv[Tuser],
argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("NICK", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s changed nick to %s",
argv[Tnick], argv[Ttext]);
else if(!strncmp("TOPIC", argv[Tcmd], 6))
snprintf(bufout, sizeof(bufout), "-!- %s changed topic to \"%s\"",
argv[Tnick], argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("KICK", argv[Tcmd], 5))
snprintf(bufout, sizeof(bufout), "-!- %s kicked %s (\"%s\")",
argv[Tnick], argv[Targ],
argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("NOTICE", argv[Tcmd], 7))
snprintf(bufout, sizeof(bufout), "-!- \"%s\")",
argv[Ttext] ? argv[Ttext] : "");
else if(!strncmp("PRIVMSG", argv[Tcmd], 8))
snprintf(bufout, sizeof(bufout), "<%s> %s",
argv[Tnick], argv[Ttext] ? argv[Ttext] : "");
if(!argv[Tchan] || !strncmp(argv[Tchan], nick, strlen(nick)))
pout(argv[Tnick], bufout);
else
pout(argv[Tchan], bufout);
}
else {
snprintf(bufout, sizeof bufout, ">< %s: %s", cmd, txt ? txt : "");
pout(usr, bufout);
if(!strncmp("NICK", cmd, 4) && !strncmp(usr, nick, sizeof nick) && txt)
strncpy(nick, txt, sizeof nick);
}
}
int
main(int argc, char *argv[])
{
main(int argc, char *argv[]) {
int i;
struct timeval tv;
struct hostent *hp;
@ -237,68 +159,51 @@ main(int argc, char *argv[])
char ping[256];
fd_set rd;
nick = fullname = getenv("USER");
for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
default:
fputs("usage: sic [-s server] [-p port] [-n nick]"
" [-k keyword] [-f fullname] [-v]\n", stderr);
exit(EXIT_FAILURE);
break;
case 's':
server = argv[++i];
break;
case 'p':
port = (unsigned short)atoi(argv[++i]);
break;
case 'n':
nick = argv[++i];
break;
case 'k':
password = argv[++i];
break;
case 'f':
fullname = argv[++i];
break;
case 'v':
fputs("sic-"VERSION", (C)opyright MMVI Anselm R. Garbe\n", stdout);
exit(EXIT_SUCCESS);
break;
strncpy(nick, getenv("USER"), sizeof nick);
for(i = 1; i < argc; i++)
if(!strncmp(argv[i], "-h", 3)) {
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)) {
if(++i < argc) password = argv[i];
}
else if(!strncmp(argv[i], "-v", 3))
eprint("sic-"VERSION", (C)opyright MMVI Anselm R. Garbe\n");
else
eprint("usage: sic [-h host] [-p port] [-n nick] [-k keyword] [-v]\n");
/* init */
if((srv = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "sic: cannot connect server '%s'\n", server);
exit(EXIT_FAILURE);
}
if (NULL == (hp = gethostbyname(server))) {
fprintf(stderr, "sic: cannot resolve hostname '%s'\n", server);
exit(EXIT_FAILURE);
}
if((srv = socket(AF_INET, SOCK_STREAM, 0)) < 0)
eprint("sic: cannot connect host '%s'\n", host);
if(NULL == (hp = gethostbyname(host)))
eprint("sic: 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);
fprintf(stderr, "sic: cannot connect server '%s'\n", server);
exit(EXIT_FAILURE);
eprint("sic: cannot connect host '%s'\n", host);
}
/* login */
if(password)
snprintf(bufout, sizeof(bufout),
snprintf(bufout, sizeof bufout,
"PASS %s\r\nNICK %s\r\nUSER %s localhost %s :%s\r\n",
password, nick, nick, server, fullname);
password, nick, nick, host, nick);
else
snprintf(bufout, sizeof(bufout), "NICK %s\r\nUSER %s localhost %s :%s\r\n",
nick, nick, server, fullname);
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", server);
snprintf(ping, sizeof ping, "PING %s\r\n", host);
channel[0] = 0;
setbuf(stdout, NULL); /* unbuffered stdout */
for(;;) {
for(;;) { /* main loop */
FD_ZERO(&rd);
FD_SET(0, &rd);
FD_SET(srv, &rd);
@ -308,32 +213,25 @@ main(int argc, char *argv[])
if(i < 0) {
if(errno == EINTR)
continue;
perror("sic: error on select()");
exit(EXIT_FAILURE);
} else if(i == 0) {
if(time(NULL) - trespond >= PINGTIMEOUT) {
pout(server, "-!- sic shutting down: parseing timeout");
exit(EXIT_FAILURE);
}
eprint("sic: error on select()");
}
else if(i == 0) {
if(time(NULL) - trespond >= PINGTIMEOUT)
eprint("sic shutting down: parse timeout");
write(srv, ping, strlen(ping));
continue;
}
if(FD_ISSET(srv, &rd)) {
if(getline(srv, sizeof(bufin), bufin) == -1) {
perror("sic: remote server closed connection");
exit(EXIT_FAILURE);
}
if(getline(srv, sizeof bufin, bufin) == -1)
eprint("sic: remote host closed connection");
parsesrv(bufin);
trespond = time(NULL);
}
if(FD_ISSET(0, &rd)) {
if(getline(0, sizeof(bufin), bufin) == -1) {
perror("sic: broken pipe");
exit(EXIT_FAILURE);
}
if(getline(0, sizeof bufin, bufin) == -1)
eprint("sic: broken pipe");
parsein(bufin);
}
}
return 0;
}