/*
* display.c: routines that proceeds the data from a server and format it.
*
* Copyright(c) 1997-2000 - All Rights Reserved
*
* See the COPYRIGHT file.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: display.c,v 1.59 2001/03/14 19:50:20 kalt Exp $";
#endif
#include "os.h"
#include "struct.h"
#include "term.h"
#include "window.h"
#include "format.h"
#include "option.h"
#include "server.h"
#include "utils.h"
extern struct server_ *server;
extern char need_collect;
extern char tab_add(char *, char *);
extern char space_add(char *, char *);
extern char sic_ctcp(char *, char, char *);
extern void url_find(char *, char *, char *);
extern void yow_back(char *, char *);
static char outp[1024];
static u_char outa[1024];
static char *s, *c, *d, *p;
static int n;
/* locals is currently unused */
static char toself, locals, froms, fromv, toch, showid;
static char nick[10], user[11], host[64];
/*
* "RAW" options
* %d recipient
* %m command
* %o origin (nick!user@host or server.name)
* %p parameters
*
* blah
* %B bold on/off
* %R reverse video on/off
* %U underline on/off
*
* "processed" options
* %I tabkey identity (%u@%h)
* %n!%u@%h
* %N numeric (alias for command??)
* %s server name the message originated from
* %S server name you are connected to
*/
static int
sic_sprintf(buffer, attr, format, channel, flags)
char *buffer, *format, *channel;
u_char *attr;
unsigned int *flags;
{
char *wp = buffer;
u_char video = 0, vplus = 0, anyattr = 0, checknow = 0, dobeep = 0;
int align;
*wp = '\0';
while (*format)
{
while (*format && *format != '%')
{
*wp++ = *format++;
*attr++ = video;
if (video)
anyattr = 1;
}
if(*format++)
{
if (align = atoi(format))
{
if (align < 0)
format++;
while (isdigit((int) *format))
format++;
}
*wp = '\0';
switch (*format++)
{
case '%':
*wp++ = '%'; *attr++ = video;
*wp = '\0';
align = 0;
break;
case 'B':
video ^= TERM_BOLD;
align = 0;
break;
case 'c':
strcat(buffer, d);
break;
case 'd':
strcat(buffer, d);
break;
case 'h':
strcat(buffer, host);
break;
case 'I':
if (showid)
{
strcat(buffer, user);
strcat(buffer, "@");
strcat(buffer, host);
}
break;
case 'm':
strcat(buffer, c);
break;
case 'n':
strcat(buffer, nick);
get_ignore(s, channel, flags);
if (*flags & LOG_HIGHLIGHT && !(video & TERM_STANDOUT))
video |= (vplus = TERM_STANDOUT);
break;
case 'N':
sprintf(wp, "%3.3d", n);
break;
case 'p':
if (*format != '{')
strcat(buffer, p);
else
{
strcat(buffer, sic_split(p, format));
if (index(format, '}'))
format = index(format, '}') + 1;
}
if (channel && (*flags & LOG_PUBLIC)
&& (*flags & (LOG_MSG|LOG_NOTICE)))
checknow = 1;
break;
case 'o':
strcat(buffer, s);
get_ignore(s, channel, flags);
if (*flags & LOG_HIGHLIGHT && !(video & TERM_STANDOUT))
video |= (vplus = TERM_STANDOUT);
break;
case 'R':
video ^= TERM_STANDOUT;
align = 0;
break;
case 's':
if (froms)
strcat(buffer, s);
else
strcat(buffer, "żżż");
break;
case 'S':
if (server)
strcat(buffer, server->sname);
else
strcat(buffer, "<how the hell did we get here?>");
break;
case 'u':
strcat(buffer, user);
break;
case 'U':
video ^= TERM_UNDERLINE;
align = 0;
break;
default:
*wp++ = (char) 191; *attr++ = TERM_STANDOUT; anyattr = 1;
*wp = '\0';
align = 0;
term_beep();
break;
}
if (align && *wp != '\0')
{
if (align > 0)
if (strlen(wp) < align)
{
char *where = wp;
align -= strlen(wp);
where += strlen(wp);
while (align--)
*where++ = ' ';
*where = '\0';
}
else
wp[align] = '\0';
else
if (strlen(wp) < -align)
{
char *where = wp - align;
char *end = wp + strlen(wp);
while (end >= wp)
*where-- = *end--;
while (where >= wp)
*where-- = ' ';
}
else
wp[-align] = '\0';
}
if (checknow)
{
char *where = wp, how = 0;
u_char extra;
int len;
checknow = 0;
len = get_keyword(channel, &where, &how, flags);
if (how & K_BEEP)
dobeep = 1;
if (how & K_SPACE && *flags & LOG_PUBLIC)
space_add(nick, "");
if (len || video)
anyattr = 1;
while (*wp != '\0')
{
extra = 0;
/* check for ircii rendering junk */
if (*wp == ctrl('_'))
extra = TERM_EUNDERLINE;
else if (*wp == ctrl('V'))
extra = TERM_ESTANDOUT;
else if (*wp == ctrl('B'))
extra = TERM_EBOLD;
if (extra)
{
video ^= extra;
extra = TERM_EHIDE;
anyattr = 1;
}
/* keywords */
*attr = video | extra;
if (where)
{
if (wp >= where && (wp - where) < len)
*attr |= (how & 0x0F);
if ((wp - where) == len)
{
where = wp;
if (len = get_keyword(channel, &where, &how, flags))
anyattr = 1;
if (how & K_BEEP)
dobeep = 1;
if (how & K_SPACE && *flags & LOG_PUBLIC)
space_add(nick, "");
}
}
wp++;
attr++;
}
}
else
{
while (*wp != '\0')
{
wp++;
*attr++ = video;
}
if (video)
anyattr = 1;
}
if (vplus)
{
video ^= vplus;
vplus = 0;
}
}
else
break;
}
*wp = '\0';
if (dobeep && !(*flags & LOG_IGNORE))
term_beep();
return anyattr;
}
/* sic_splitorig: splits n!u@h */
static void
sic_splitorig(origin)
char *origin;
{
strcpy(nick, "<bogus>"); strcpy(user, "<bogus>"); strcpy(host, "<bogus>");
if (s && index(s, '!'))
{
char *ch = s, *ptr;
ptr = nick;
while (*ch != '!')
*ptr++ = *ch++;
*ptr = '\0'; ch++;
ptr = user;
while (*ch != '@')
*ptr++ = *ch++;
*ptr = '\0'; ch++;
ptr = host;
while (*ch)
*ptr++ = *ch++;
*ptr = '\0';
}
}
void
sic_format(sender, cmd, dest, para)
char *sender, *cmd, *dest, *para;
{
int fmt = -1;
unsigned int flags = 0;
int tocurrent = 1, ctcp = 0, yow = 0;
char tochannel[512];
assert(server);
locals = toself = froms = fromv = toch = showid = 0;
s = sender;
if (option(server->sopt, S_DCC))
{
char name[11];
strcpy(user, "<bogus>"); strcpy(host, "<bogus>");
strcpy(nick, server->nick);
flags = LOG_USER|LOG_PRIVATE|LOG_DCC;
fmt = F_DCC;
p = para;
sprintf(name, "=%s", server->nick);
tab_add(name, "");
}
else
{
sic_splitorig(s);
c = cmd;
d = (*dest == ':') ? dest+1 : dest; /* hmmpf */
if (p = index(d, '\007')) /* special case, 2.9 join with ^G */
{
assert (*para == '\0');
*p = '\0';
para = p+1;
}
p = para;
n = atoi(cmd);
if (n != 1)
{
/*
* simple analysis of origin and destination
* It will be used to define the message appearance,
* as well as the window where it is to be displayed.
*/
if (!strcasecmp(s, server->sname))
locals = 1; /* from local server */
if (!strcasecmp(d, server->nick))
{
toself = 1; /* to the user */
flags |= LOG_PRIVATE;
}
else
if (toch = map_c2w(d)) /* to a channel? */
{
flags |= LOG_PUBLIC;
strcpy(tochannel, d);
}
else
flags |= LOG_BROADCAST;
if (index(s, '!'))
flags |= LOG_USER;
else
if (!index(s, '@'))
{
froms = 1; /* from a server */
flags |= LOG_SERVER;
}
else
{
fromv = 1; /* from a service */
flags |= LOG_SERVICE;
}
}
/*
* ugly huge statement, but everything that has to be parsed is here,
* ...
*/
if (!strcasecmp("NOTICE", cmd))
{
time_t ts;
flags |= LOG_NOTICE;
fmt = fromv ? F_VNOTICE :
froms ? F_SNOTICE :
toch ? F_CNOTICE : F_UNOTICE;
if (fromv)
tab_add(sender, NULL);
tocurrent = 0;
if (!fromv && !froms && sscanf(p, "\001PING %lu\001", &ts) == 1)
{
flags |= LOG_HIDE;
vsic_slog(LOG_CLIENT, "--- CTCP ping reply from %s: %s", nick,
sic_tdiff(time(NULL)-ts, 0));
}
}
else if (!strcasecmp("PRIVMSG", cmd))
{
flags |= LOG_MSG;
fmt = froms ? F_SMSG :
toch ? F_CMSG : F_UMSG;
tocurrent = 0;
if (strncmp("\001DCC ", p, 5) && index(p, '\001'))
{
ctcp = 1;
if (strncmp("\001ACTION ", p, 8)
&& !get_option(Z_VCTCP, (toch) ? tochannel : NULL))
flags |= LOG_HIDE;
}
if (get_option(Z_URL, (toch) ? tochannel : NULL))
url_find(sender, (toch) ? tochannel : NULL, p);
if (toch && !froms && !fromv && !get_option(Z_NOYOW, tochannel))
yow = 1;
if (!froms && !toch)
if (strncmp("\001DCC ", p, 5))
{
char uh[256];
sprintf(uh, "%s@%s", user, host);
if (strncmp("\001ACTION ", p, 8)
&& get_option(Z_VCTCP, (toch) ? tochannel : NULL))
showid = tab_add(nick, uh);
}
else
{
char *file, *ip, *size;
signed char type = -1;
unsigned long ipaddr, sz;
unsigned int portnb;
if (!strncmp("CHAT ", p+5, 5))
type = 0;
else if (!strncmp("SEND ", p+5, 5))
type = 1;
else
vsic_slog(LOG_CLIENT, "--- Unknown DCC request from %s:",
nick);
if (type >= 0)
if (file = index(p+5, ' '))
if (ip = index(file+1, ' '))
if (size = index(ip+1, ' '))
{
*ip = '\0';
flags |= LOG_HIDE;
sscanf(ip+1, "%lu %u", &ipaddr, &portnb);
if (size = index(size+1, ' '))
sscanf(size, "%lu", &sz);
sic_dcc(type, nick, ipaddr, portnb, file+1,
sz);
*ip = ' ';
}
}
}
else
{
flags |= LOG_CRAP;
if (n)
{
char *ch1, *ch2;
fmt = F_NUM;
switch (n)
{
case 1:
if (server->sname)
free(server->sname);
server->sname = strdup(s);
if (server->nick)
free(server->nick);
server->nick = strdup(d);
if (!strcasecmp(s, server->sname))
locals = 1;
if (!strcasecmp(d, server->nick))
toself = 1;
unset_option(server->sopt, S_MOTD);
break;
case 2:
ch1 = strstr(p, "version");
if (ch1 == NULL)
{
sic_slog(LOG_CLIENT,
"--- Unable to determine protocol type.");
server->protocol = P_IRC;
}
else
{
unsigned char proto;
ch1 += 8;
if (*ch1 == 'u')
{
proto = P_U;
ch2 = "Undernet";
}
else if (!strncmp(ch1, "dal", 3))
{
proto = P_D;
ch2 = "Dalnet";
}
else if (strstr(ch1, "+CSr") || strstr(ch1, "/hybrid"))
{
proto = P_E;
ch2 = "EFnet";
}
else
{
proto = P_IRC;
ch2 = "IRC";
}
if (server->protocol == 0)
vsic_slog(LOG_CLIENT, "--- Protocol: %s", ch2);
else if (proto != server->protocol)
vsic_slog(LOG_CLIENT,"--- Protocol changed: %s",ch2);
server->protocol = proto;
}
break;
case 221: /* umodes */
flags |= LOG_HIDE; /* see cheap trick comment below */
strcpy(server->umode, p);
break;
case 324: /* channel mode */
break;
case 353: /* names */
ch2 = p + 2; /* channel name */
*(ch1 = index(ch2, ' ')) = '\0';
strcpy(tochannel, ch2);
*ch1 = ' '; ch1 +=2;
if (map_c2w(tochannel))
{
toch = 1;
if (*ch1 == '@')
set_chop(tochannel, 2); /* inexact, caught by set_chop() */
while (ch1 && *ch1)
{
while (*ch1 == '@' || *ch1 == '+')
ch1++;
if (ch2 = index(ch1, ' '))
*ch2 = '\0';
add_member(tochannel, ch1, 1);
if (ch2)
{
*ch2 = ' ';
ch1 = ch2 + 1;
}
else
ch1 = NULL;
}
}
break;
case 366: /* end of names */
ch1 = index(p, ' '); *ch1 = '\0';
strcpy(tochannel, p); *ch1 = ' ';
if (map_c2w(tochannel))
{
toch = 1;
add_member(tochannel, NULL, 0);
}
break;
case 421: /* Unknown command */
if (!strncmp("LAME:", p, 5))
{
ch1 = index(p, ' ');
*ch1 = '\0';
join_channel(p+5, 1);
flags |= LOG_IGNORE;
*ch1 = ' ';
}
break;
case 422: /* MOTD file is missing */
case 376: /* end of MOTD */
if (!strcasecmp(s, server->sname)
&& !option(server->sopt, S_MOTD))
{
if (need_collect)
collect_channel();
rejoin_channel(NULL);
set_option(server->sopt, S_MOTD);
}
break;
case 381: /* you're now such a lamer (ircop) */
vsic_write("MODE %s", server->nick);
break;
case 403: /* no such channel */
case 442: /* not on this channel */
ch1 = index(p, ' ');
*ch1 = '\0';
if (map_c2w(p))
del_channel(p);
*ch1 = ' ';
break;
case 437: /* temporarily unavailable */
case 471: /* cannot join +l */
case 473: /* cannot join +i */
case 474: /* cannot join +b */
case 475: /* cannot join +k */
ch1 = index(p, ' ');
*ch1 = '\0';
if (map_c2w(p))
{
if (n == 475)
{
struct channel_ *ctmp;
ctmp = get_channel(p);
if (ctmp->chkey)
{
vsic_slog(LOG_CLIENT, "--- Forgetting key \"%s\" for channel %s.", ctmp->chkey, p);
free(ctmp->chkey);
ctmp->chkey = NULL;
}
}
if (get_option(Z_REJOIN, p))
rejoin_channel(p);
else
{
del_channel(p);
collect_channel();
}
}
*ch1 = ' ';
break;
}
}
else if (!strcasecmp("KILL", cmd))
fmt = F_KILL;
else if (!strcasecmp("ERROR", cmd))
{
fmt = F_ERROR;
del_channel(NULL);
}
else if (!strcasecmp("INVITE", cmd))
{
struct channel_ *ctmp;
fmt = F_INVITE;
if ((ctmp = get_channel(p)) && option(ctmp->copt, C_REJOIN))
{
vsic_slog(LOG_CLIENT, "--- Answering invitation to %s.", p);
join_channel(p, 0);
}
}
else if (!strcasecmp("JOIN", cmd))
{
fmt = F_JOIN;
assert(toch || *d == '!');
if (!toch)
{
chg_channel(d);
strcpy(tochannel, d);
flags = (flags ^ LOG_BROADCAST) | LOG_PUBLIC;
toch = 1;
}
if (!strcasecmp(nick, server->nick))
del_member(d, NULL);
else
add_member(d, nick, 0);
}
else if (!strcasecmp("PART", cmd))
{
fmt = F_PART;
del_member(d, nick);
if (!strcasecmp(nick, server->nick))
del_channel(d);
}
else if (!strcasecmp("KICK", cmd))
{
char *comment = index(p, ' ');
assert(comment);
fmt = F_KICK;
*comment = '\0';
del_member(d, p);
if (!strcasecmp(p, server->nick))
{
del_channel(d);
if (get_option(Z_REJOIN, d))
rejoin_channel(d);
}
*comment = ' ';
}
else if (!strcasecmp("TOPIC", cmd))
fmt = F_TOPIC;
else if (!strcasecmp("QUIT", cmd))
{
int split = 0;
char *wp;
fmt = F_QUIT;
flags |= LOG_MULTICAST;
del_member(NULL, nick);
toch = 0; d = "<bogus>"; p = dest;
wp = p;
while (*wp && *wp != ' ')
if (*wp++ == '.')
split = 1;
if (*wp++ && split)
{
split = 0;
while (*wp && *wp != ' ')
if (*wp++ == '.')
split = 1;
if (!split || *wp)
flags |= LOG_UQUIT;
else
flags |= LOG_SQUIT;
}
else
flags |= LOG_UQUIT;
}
else if (!strcasecmp("NICK", cmd))
{
chg_member(nick, d);
if (!strcmp(nick, server->nick))
{
free(server->nick);
server->nick = strdup(d);
}
else
flags |= LOG_MULTICAST;
fmt = F_NICK;
}
else if (!strcasecmp("MODE", cmd))
if (!strcmp(sender, server->nick))
{
strcpy(nick, sender); /* eww */
fmt = F_UMODE;
if (!index(p, '-') && server->umode[0] == '\0')
strcpy(server->umode, p);
else
vsic_write("MODE %s", server->nick); /* cheap trick */
}
else if (froms)
fmt = F_SMODE;
else
fmt = F_CMODE;
else
fmt = F_UNKNOWN;
}
}
select_active((toch) ? tochannel : NULL, tocurrent);
if (toch == 0 && (flags & (LOG_MSG|LOG_NOTICE|LOG_DCC)))
if (fromv || froms)
select_query(sender);
else
{
char tmpq[11];
sprintf(tmpq, "%s%s", (flags & LOG_DCC) ? "=" : "", nick);
select_query(tmpq);
}
if (fmt != -1)
{
char *fmt_str;
int rendering;
if (fmt == F_NUM)
fmt_str = get_nformat(n, (toch) ? tochannel : NULL);
else
fmt_str = get_format(fmt, (toch) ? tochannel : NULL);
if (*fmt_str == '\0')
{
flags |= LOG_HIDE;
fmt_str++;
}
rendering = sic_sprintf(outp, outa, fmt_str, (toch) ? tochannel : NULL,
&flags);
#if defined(HAVE_REGEXP)
rendering += get_rewrite(fmt, outp, outa, &flags,
(toch) ? tochannel : NULL);
#endif
sic_log(flags, 0, s, c, (toself) ? NULL : d, p, fmt, outp,
(rendering) ? outa : NULL);
}
else
vsic_slog(LOG_CLIENT, "Wooops: %s", cmd);
if (ctcp)
sic_ctcp(nick, toself, p);
if (yow)
yow_back(tochannel, p);
}
void
sic_reformat(log)
struct log_ *log;
{
char *fmt_str;
/* locals is unused,
toself and toch hace no use here,
showid isn't reusable :/ */
locals = toself = froms = fromv = toch = showid = 0;
n = 0;
if (log->fmt == -1 || log->fmt == F_SPLIT)
return;
s = log->origin; c = log->cmd; d = log->dest; p = log->para;
sic_splitorig(log->origin);
if (log->flags & LOG_DCC && log->origin)
{
char *at;
strncpy(nick, log->origin, 10);
at = index(nick, '@');
if (at)
*at = '\0';
else
nick[9] = '\0';
}
if (log->flags & LOG_SERVICE)
fromv = 1;
if (log->flags & LOG_SERVER)
froms = 1;
log->flags &= ~(LOG_HIGHLIGHT|LOG_KEYWORD | LOG_HIDE|LOG_IGNORE);
if (log->fmt == F_NUM)
fmt_str = get_nformat(n = atoi(log->cmd), log->dest);
else
fmt_str = get_format(log->fmt, log->dest);
if (*fmt_str == '\0')
{
fmt_str++;
log->flags |= LOG_HIDE;
}
free(log->prefix);
if (log->prefix = get_format(F_PREFIX, log->dest))
{
char pbuf[80]; /* should be more than enough */
my_cftime(pbuf, 80, log->prefix, log->ts);
log->prefix = strdup(pbuf);
}
log->plen = strlen(log->prefix);
free(log->formatted);
free(log->attributes); log->attributes = NULL;
if (sic_sprintf(outp, outa, fmt_str, log->dest, &(log->flags)))
{
log->attributes = (u_char *) malloc(log->len+1);
bcopy(outa, log->attributes, log->len+1);
}
log->formatted = strdup(outp);
log->len = strlen(log->formatted);
}
syntax highlighted by Code2HTML, v. 0.9.1