/*
* window.c: routines for the windows manipulations
*
* See the COPYRIGHT file.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: window.c,v 1.69 2001/03/14 19:50:21 kalt Exp $";
#endif
#include "os.h"
#include "struct.h"
#include "term.h"
#include "format.h"
#include "option.h"
#include "config.h"
#include "server.h"
#include "utils.h"
#define sic_slog(x, y) sic_log(x, 0, NULL, NULL, NULL, NULL, -1, y, NULL)
void vsic_log(unsigned int, char *, char *, char *,
char *, char, char *, ...);
void sic_redowin(int);
void collect_channel();
extern void sic_reformat(struct log_ *);
extern struct server_ *server;
char need_collect = 0;
struct window_ *wlist = NULL, *current, *oldcurrent, *active;
static struct channel_ *clist = NULL;
static int refresh_status;
static void display_line(int, struct log_ *);
#define LNCNTWP(x) (((x->plen + x->len-1) / (CO-1)) + 1)
#define LNCNTNP(x) (((x->len-1) / (CO-1)) + 1)
#define LNCNT(x) ((option(current->wopt, W_NOPREFIX)) ? LNCNTNP(x) : \
LNCNTWP(x))
#define PLNLEN(x) (option(current->wopt, W_NOPREFIX) ? CO-1 : \
CO-1-x->plen)
/*
* GENERAL USEFUL FUNCTIONS
*/
struct channel_ *
get_channel(name)
char *name;
{
struct channel_ *ctmp = clist;
if (name)
while (ctmp && (strcasecmp(name, ctmp->chname) || server != ctmp->on->via))
ctmp = ctmp->nextc;
else
ctmp = current->defchan;
return ctmp;
}
/*
* LOGGING
*/
/* sic_log: ``display'' a string in the ``active'' window
* the string is added to the lastlog
* it will only really be displayed if the ``active == current''
*
* (the active window must be set before calling this window)
* the second parameter is optional, it is used for video attributes
*/
void
sic_log(flags, ts, o, c, d, p, fmt, str, attr)
char *str, *attr, *o, *c, *d, *p, fmt;
unsigned int flags;
time_t ts;
{
struct log_ *new;
assert (active && current);
if (flags & LOG_MULTICAST)
{
struct window_ *wtmp = wlist, *oldactive = active;
flags &= ~LOG_MULTICAST;
while (wtmp)
{
if (option(wtmp->wopt, W_MCAST))
{
active = wtmp;
sic_log(flags, ts, o, c, d, p, fmt, str, attr);
}
wtmp = wtmp->nextw;
}
active = oldactive; /* probably useless precaution */
return;
}
/* smart split handling, kind of weird to put it here :/ */
if (flags & LOG_SQUIT)
if (!active->split || strcasecmp(active->split, p)
|| active->split_t < time(NULL))
{
char *wp;
if (active->split)
free(active->split);
active->split = strdup(p);
active->split_t = time(NULL) + 15;
wp = index(p, ' ');
assert(wp);
*wp = '\0';
vsic_log((flags & ~LOG_SQUIT) | LOG_SPLIT, "<internal>", "SPLIT",
d, active->split, F_SPLIT, get_format(F_SPLIT, d), p, wp+1);
*wp = ' ';
}
#if 0
if (o && ((flags & (LOG_PRIVATE|LOG_MSG)) == (LOG_PRIVATE|LOG_MSG)
|| (flags & (LOG_PRIVATE|LOG_DCC)) == (LOG_PRIVATE|LOG_DCC)))
#else
if (o && get_flag(flags, d, 10))
#endif
set_option(active->wopt, W_ACTIVITY);
new = (struct log_ *) malloc(sizeof(struct log_));
bzero(new, sizeof(struct log_));
if (active->llog)
active->llog->nextl = new;
else
active->flog = new;
new->prevl = active->llog;
new->flags = flags;
if (o)
new->origin = strdup(o);
if (c)
new->cmd = strdup(c);
if (d)
new->dest = strdup(d);
if (p)
new->para = strdup(p);
new->fmt = fmt;
new->ts = (ts) ? ts : time(NULL);
if (new->prefix = get_format(F_PREFIX, d))
{
char pbuf[80]; /* should be more than enough */
my_cftime(pbuf, 80, new->prefix, new->ts);
new->prefix = strdup(pbuf);
}
new->plen = strlen(new->prefix);
new->formatted = strdup(str);
new->len = strlen(str);
if (attr)
{
new->attributes = (u_char *) malloc(strlen(str)+1);
bcopy(attr, new->attributes, strlen(str)+1);
}
active->llog = new;
/*
* display the new line if it is the current window and we're at the bottom.
* could be done using the scrolling routines?
*/
if (active->ldisp == new->prevl && !get_option(Z_HOLD, d))
{
active->ldisp = new; /* not scrolling anything */
active->ltrunc = 0; /* line is entirely displayed */
if (active == current)
if (get_flag(flags, d, active->fnb) || get_option(Z_SHOWALL, d))
{
int line_count = LNCNT(new);
int i = 0;
if (term_scroll(0, LI-3, line_count) < 0)
sic_redowin(0);
else
while (i < line_count)
{
term_move_cursor(0, LI-2-line_count+i);
display_line(i++, new);
}
}
}
sic_logfile(new);
}
void
vsic_log(unsigned int flags, char *o, char *c, char *d, char *p, char fmt,
char *format, ...)
{
char buffer[1024];
va_list va;
va_start(va, format);
vsprintf(buffer, format, va);
va_end(va);
sic_log(flags, 0, o, c, d, p, fmt, buffer, NULL);
}
void
vsic_slog(unsigned int flags, char *format, ...)
{
char buffer[1024];
va_list va;
va_start(va, format);
vsprintf(buffer, format, va);
va_end(va);
sic_slog(flags, buffer);
}
/* sic_dlog: delete log entries */
void
sic_dlog(force)
int force;
{
struct log_ *del, *tmpl = current->flog;
time_t now = time(NULL);
unsigned int cnt = 0, dcnt = 0;
if (!(current->flog && current->flog->flags & LOG_DEL) && (force == 0))
return;
while (tmpl)
{
tmpl = (del = tmpl)->nextl;
cnt++;
if (force
|| ((del->flags & LOG_DEL)
&& ((del->flags & LOG_QUICKDEL) || now > del->delts)))
{
if (del->prevl)
del->prevl->nextl = del->nextl;
if (del->nextl)
del->nextl->prevl = del->prevl;
if (current->flog == del)
current->flog = del->nextl;
if (current->llog == del)
current->llog = del->nextl;
if (current->ldisp == del)
current->ldisp = del->nextl;
if (del->origin)
free(del->origin);
if (del->cmd)
free(del->cmd);
if (del->dest)
free(del->dest);
if (del->para)
free(del->para);
if (del->prefix)
free(del->prefix);
if (del->formatted)
free(del->formatted);
if (del->attributes)
free(del->attributes);
free(del);
dcnt++;
}
}
#if defined(DEBUG)
/* it's a memory leak when called from sic_wkill() */
vsic_slog(LOG_DEBUG, "sic_dlog(%d): %u out of %u", force, dcnt, cnt);
#endif
}
/* sic_clog: clear the lastlog for the current window */
void
sic_clog(force)
int force;
{
struct log_ *tmpl = current->llog;
time_t now = time(NULL) + 3600;
unsigned int cnt = 0;
unset_option(current->custw.zopt_on, Z_UNDEL);
if (force)
sic_dlog(1);
else
{
while (tmpl && !(tmpl->flags & LOG_DEL))
{
tmpl->flags |= LOG_DEL;
tmpl->delts = now;
tmpl = tmpl->prevl;
cnt++;
}
vsic_slog(LOG_DEBUG, "sic_clog(0): %u", cnt);
sic_dlog(0); /* not very efficient */
}
}
/* cmd_seen: internal whowas */
int
cmd_seen(p)
char *p;
{
char *c;
int max = 3, skip = 0, hit = 0;
struct log_ *ltmp = active->llog, *found[100];
if (!*p)
return -1;
if (c = index(p, ' '))
{
*c++ = '\0';
max = atoi(c);
if (c = index(c, ' '))
skip = atoi(c+1);
}
if (max < 0 || skip < 0)
return -4;
if (max > 100)
max = 100;
while (ltmp && (max == 0 || hit < max))
{
if (ltmp->flags & LOG_USER && rmatch(p, ltmp->origin))
{
int i = hit;
if (hit)
{
for (i = 0; i < hit; i++)
{
if (strcasecmp(found[i]->origin, ltmp->origin))
continue;
if (found[i]->dest == NULL && ltmp->dest == NULL)
continue;
if (found[i]->dest == NULL || ltmp->dest == NULL)
break;
if (!strcasecmp(found[i]->dest, ltmp->dest))
break;
}
}
if (i == hit)
{
found[hit++] = ltmp;
if (skip == 0)
vsic_slog(LOG_CLIENT, "--- Seen %s%s%s.", ltmp->origin,
(ltmp->dest) ? " on " : "",
(ltmp->dest) ? ltmp->dest : "");
else
skip--;
}
}
ltmp = ltmp->prevl;
}
if (hit == 0)
vsic_slog(LOG_CLIENT, "--- No match found.");
return 0;
}
/* cmd_lastlog: redisplay from the /lastlog */
int
cmd_lastlog(p)
char *p;
{
unsigned int mask = LOG_ALL;
char *pattern = (*p) ? p : NULL;
struct log_ *tmpl = current->flog;
if (!strncmp("0x", p, 2))
{
sscanf(p, "%x", &mask);
pattern = index(p, ' ');
}
vsic_slog(LOG_CLIENT|LOG_LASTLOG, "--- Lastlog:");
while (tmpl)
{
if ((tmpl->flags & LOG_LASTLOG) == 0 && (tmpl->flags & mask))
if (!pattern || rmatch(pattern, tmpl->prefix)
|| strcasestr(tmpl->formatted, pattern))
{
sic_log(tmpl->flags | LOG_LASTLOG, tmpl->ts, tmpl->origin,
tmpl->cmd, tmpl->dest, tmpl->para, tmpl->fmt,
tmpl->formatted, tmpl->attributes);
}
tmpl = tmpl->nextl;
}
vsic_slog(LOG_CLIENT|LOG_LASTLOG, "--- End of lastlog.");
return 0;
}
/* sic_wreformat: reformat the log */
void
sic_wreformat(p)
char *p;
{
struct log_ *tmpl = current->llog;
while (tmpl)
{
sic_reformat(tmpl);
tmpl = tmpl->prevl;
}
sic_redowin(1);
vsic_slog(LOG_CLIENT|LOG_LASTLOG, "--- Reformatted lastlog.");
}
/* display_status: formats the status line */
/* this should be customizable */
void
display_status()
{
static char st[160];
struct channel_ *ctmp = clist;
struct window_ *wtmp = wlist;
int chcnt = 0;
char chbuf[6] = "", actbuf[40] = "", tbuf[6],
buf[160]; /* hmmpf */
my_strftime(tbuf, 6, "%H:%M");
while (ctmp)
{
if (ctmp->on == current)
chcnt++;
ctmp = ctmp->nextc;
}
if (chcnt > 1)
sprintf(chbuf, " %d (%d)", option(current->defchan->copt, C_JOINED) ?
current->defchan->member_cnt : -1, chcnt);
else if (chcnt == 1)
sprintf(chbuf, " %d", option(current->defchan->copt, C_JOINED) ?
current->defchan->member_cnt : -1);
if (current->ldisp == current->llog)
{
unset_option(current->wopt, W_KEYWORD);
unset_option(current->wopt, W_ACTIVITY);
}
while (wtmp)
{
if (option(wtmp->wopt, W_KEYWORD) || option(wtmp->wopt, W_ACTIVITY))
{
char blah[4];
if (*actbuf)
strcat(actbuf, ",");
sprintf(blah, "%d%s", wtmp->ref, /* this stinks. hmmpf! */
option(wtmp->wopt, W_ACTIVITY) ? "+" : "");
strcat(actbuf, blah);
}
wtmp = wtmp->nextw;
}
sprintf(buf, "sic[%d:%d%s] %s%s%s%s%s %s%s%s%s (%s) [%s] %s%s%s",
current->ref, current->fnb,
(option(current->custw.zopt_on, Z_UNDEL)) ? "u" : "",
(current->llog == current->ldisp) ? "" : "*more* ",
(*actbuf) ? "(" : "", actbuf, (*actbuf) ? ") " : "",
tbuf,
(current->via && current->via->nick) ? current->via->nick : "",
(*current->query) ? " [>" : "", current->query,
(*current->query) ? "<]" : "",
(current->via) ? current->via->umode : "",
(current->via) ? current->via->sname : "no server",
(current->defchan && option(current->defchan->copt,C_CHOP)) ? "@":"",
(current->defchan) ? current->defchan->chname : "", chbuf);
if (refresh_status || strcmp(buf,st))
{
refresh_status = 0;
active = current;
server = current->via;
term_status(buf, !get_option(Z_RSTATUS, (current->defchan) ?
current->defchan->chname : NULL));
term_clreol();
strcpy(st, buf);
}
}
/*
* WINDOW MANAGEMENT
*/
static int win_cnt = 0, win_max = -1;
static struct window_ * win_map[20];
/* sic_newwin: creates a new window */
void
sic_newwin()
{
struct window_ *new, *w = wlist;
int i;
oldcurrent = new = (struct window_ *) malloc(sizeof(struct window_));
bzero(new, sizeof(struct window_));
for (i = 0; i <= win_max; i++)
if (win_map[i] == NULL)
break;
if (i == win_max && win_map[i] != NULL)
i++;
win_map[i] = new;
if (i > win_max)
win_max = i;
win_cnt += 1;
new->ref = i;
new->fnb = 1;
/* make first window created current */
if (wlist == NULL)
{
wlist = new;
/*set_option(new->wopt, W_DEFAULT);*/
set_option(new->wopt, W_DCC);
active = current = new;
}
else
{
while (w->nextw)
w = w->nextw;
w->nextw = new;
}
new->via = current->via; /* new can be current */
vsic_slog(LOG_DEBUG, "assigned window %d to %s.", new->ref, (server) ?
server->sname : "<unknown>");
/*set_option(new->custw.zopt_on, Z_SHOWALL);*/
}
/* sic_swin: re/assigns server/window relationship
* 1 -> assign new/reassign
* 2 -> free
*/
char
sic_swin(what)
char what;
{
struct window_ *wtmp = wlist, *act = active;
struct server_ *olds = current->via;
char cnt = 0, merge = 0;
switch (what)
{
case 1:
/* where is this info going? */
if (option(server->sopt, S_CONNECTED)
|| option(server->sopt, S_CONNECTING))
{
if (current->defchan)
{
sic_slog(LOG_CLIENT,
"--- Part channels in current window first!");
break;
}
current->via = server;
unset_option(current->wopt, W_DEFAULT);
cnt++;
vsic_slog(LOG_DEBUG, "(re)assigned window %d from %s to %s.",
current->ref, (olds) ? olds->sname : "<none>",
server->sname);
}
else
{
/* are we merging two groups of windows? */
while (wtmp)
{
if (wtmp->via == server)
merge += 1;
wtmp = wtmp->nextw;
}
wtmp = wlist;
if (merge)
{
/*
** merging can get really tricky because of channels,
** so only simple merge cases are allowed.
*/
while (wtmp)
{
if (wtmp->via == olds && wtmp->defchan)
cnt += 1;
wtmp = wtmp->nextw;
}
if (cnt)
{
vsic_slog(LOG_CLIENT, "--- %d window(s) from this group have channels bound to them!", cnt);
break;
}
wtmp = wlist;
}
while (wtmp)
{
if (wtmp->via == olds)
{
if (server)
{
active = wtmp;
vsic_slog(LOG_CLIENT,
"--- Window now bound to %s %d [%s]",
server->sname, server->port,
(server->ip) ? server->ip : "???");
}
active = current;
vsic_slog(LOG_DEBUG, "(re)assigned window %d from %s to %s.",
wtmp->ref, (olds) ? olds->sname : "<none>",
server->sname);
wtmp->lasts = wtmp->via;
wtmp->via = server;
cnt++;
}
wtmp = wtmp->nextw;
}
set_option(current->wopt, W_DEFAULT);
active = current;
wtmp = wlist;
while (wtmp)
{
if (current != wtmp && wtmp->via == server
&& option(wtmp->wopt, W_DEFAULT))
unset_option(current->wopt, W_DEFAULT);
wtmp = wtmp->nextw;
}
if (option(current->wopt, W_DEFAULT))
{
vsic_slog(LOG_DEBUG, "window %d is default for %s.",
current->ref, server->sname);
}
}
server = olds; cmd_server(NULL); server = current->via;
vsic_slog(LOG_CLIENT, "--- %sassigned %d window(s) to %s.",
(olds) ? "re" : "", cnt, server->sname);
break;
case 2:
if (olds == NULL)
sic_slog(LOG_CLIENT, "--- Window is already free.");
else
{
while (wtmp)
{
if (wtmp->via == olds)
{
cnt++;
if (option(current->wopt, W_DEFAULT) && current != wtmp)
{
unset_option(current->wopt, W_DEFAULT);
set_option(wtmp->wopt, W_DEFAULT);
vsic_slog(LOG_CLIENT,
"--- %d is now the default window for %s.",
wtmp->ref, wtmp->via->sname);
}
}
wtmp = wtmp->nextw;
}
if ((cnt == 1
&& (option(current->via->sopt, S_CONNECTING)
|| option(current->via->sopt, S_CONNECTED)))
|| !wlist->nextw)
sic_slog(LOG_CLIENT, "--- Cannot free last window.");
else
{
struct channel_ *ctmp = clist;
if (cnt == 1)
cmd_server(NULL);
cnt = 0;
while (ctmp)
{
if (ctmp->on == current)
cnt++;
ctmp = ctmp->nextc;
}
if (cnt)
vsic_slog(LOG_CLIENT,
"--- %d channel%s bound to this window!", cnt,
(cnt == 1) ? " is" : "s are");
else
{
current->lasts = current->via;
current->via = NULL;
}
}
}
break;
default:
abort(); /* never */
}
active = act;
return cnt;
}
int
sic_scnt(svr)
struct server_ *svr;
{
struct window_ *wtmp = wlist;
int i = 0;
while (wtmp)
{
if (wtmp->via == svr)
i += 1;
wtmp = wtmp->nextw;
}
return i;
}
void
sic_wkill()
{
sic_swin(2);
if (current->via == NULL)
{
struct window_ **wtmp = &wlist, *wdel;
sic_clog(1);
while (*wtmp != current)
wtmp = &((*wtmp)->nextw);
wdel = *wtmp;
*wtmp = wdel->nextw;
if (wdel->split)
free(wdel->split);
win_map[wdel->ref] = NULL;
if (wdel->ref == win_max)
while (win_map[--win_max] == NULL);
if (wdel->defchan)
free(wdel->defchan);
if (wdel->split)
free(wdel->split);
if (option(wdel->wopt, W_DCC))
set_option(wdel->nextw->wopt, W_DCC);
opt_free(&(wdel->custw));
free(wdel);
win_cnt -= 1;
if (oldcurrent == wdel)
{
oldcurrent = NULL;
current = wlist;
}
else
current = oldcurrent;
sic_redowin(1);
}
}
void
sic_wlist()
{
struct window_ *wtmp;
struct channel_ *ctmp;
char buffer[1024];
int i;
vsic_slog(LOG_CLIENT, "--- %d window%s:", win_cnt, (win_cnt > 1) ? "s" : "");
for (i = 0; i <= win_max; i++)
{
if ((wtmp = win_map[i]) == NULL)
continue;
vsic_slog(LOG_CLIENT, "--- %2d: %s%s%20s %20s %s", i,
(option(wtmp->wopt, W_DCC)) ? "=" : " ",
(option(wtmp->wopt, W_DEFAULT)) ? "*" : " ",
(wtmp->via) ? wtmp->via->sname : "<unbound>",
(wtmp->defchan) ? wtmp->defchan->chname : "", wtmp->query);
ctmp = clist;
buffer[0] = '\0';
while (ctmp)
{
if (ctmp->on == wtmp)
{
if (option(ctmp->copt, C_JOINED))
strcat(buffer, ">");
if (option(ctmp->copt, C_CHOP))
strcat(buffer, "@");
if (option(ctmp->copt, C_REJOIN))
strcat(buffer, "*");
strcat(buffer, ctmp->chname);
strcat(buffer, " ");
}
ctmp = ctmp->nextc;
}
if (buffer[0])
vsic_slog(LOG_CLIENT, "--- %s", buffer);
#if defined(DEBUG)
{
struct log_ *ltmp;
int total = 0, del = 0, delexp = 0;
time_t now = time(NULL);
for (ltmp = wtmp->flog; ltmp; ltmp = ltmp->nextl)
{
total += 1;
if (ltmp->flags & LOG_DEL)
{
del += 1;
if (now > ltmp->delts)
delexp += 1;
}
}
vsic_slog(LOG_DEBUG,
"--- Lines: %d total, %d to be deleted, %d expired",
total, del, delexp);
}
#endif
}
}
void
sic_wch(new)
signed char new;
{
struct window_ *swap;
int i = 0;
assert( new >= 0 && new < 10 );
if (swap = win_map[(int)new])
swap->ref = current->ref;
win_map[current->ref] = swap;
current->ref = new;
win_map[(int)new] = current;
for (i=0; i < 20; i++)
if (win_map[i])
win_max = i;
}
void
sic_wdcc()
{
struct window_ *wtmp = wlist;
while (wtmp)
{
unset_option(wtmp->wopt, W_DCC);
wtmp = wtmp->nextw;
}
set_option(active->wopt, W_DCC);
}
void
sic_wchannel(chan)
char *chan;
{
struct channel_ *ctmp, *mtmp = get_channel(chan);
struct window_ *curw, *act = active;
assert(mtmp);
curw = mtmp->on;
mtmp->on = active;
if (active->defchan == NULL)
active->defchan = mtmp;
if (curw->defchan == mtmp)
{
ctmp = clist;
while (ctmp)
{
if (ctmp->on->via == server && ctmp->on == curw && ctmp != mtmp)
break;
ctmp = ctmp->nextc;
}
if (ctmp)
curw->defchan = ctmp;
}
active = curw;
vsic_slog(LOG_CLIENT, "--- Channel %s moved to window %d.", chan,
act->ref);
active = act;
}
/*
* DISPLAYING
*/
/* display_line: display one physical line from a log entry */
static void
display_line(lnb, line)
int lnb;
struct log_ *line;
{
u_char mask;
assert(lnb >= 0 && lnb <= LNCNT(line));
mask = TERM_CLIENT; /* should be toggable */
if (option(current->custw.zopt_on, Z_IRCII))
mask |= TERM_ECLIENT | TERM_EHIDE;
if (option(current->custw.zopt_on, Z_NOCOLOR))
mask |= TERM_HIDE;
if (!get_flag(line->flags, line->dest, active->fnb))
term_underline_on(); /* Z_SHOWALL is on */
if (lnb == 0)
{
if (line->prefix && !option(current->wopt, W_NOPREFIX))
{
if (line->flags & LOG_IGNORE)
term_underline_on();
if (current->searchstr
&& strcasestr(line->formatted, current->searchstr))
term_bold_on();
term_puts(line->prefix, line->plen);
if (current->searchstr
&& strcasestr(line->formatted, current->searchstr))
term_bold_off();
if (line->flags & LOG_IGNORE)
term_underline_off();
}
term_putes(line->formatted, PLNLEN(line), line->attributes, mask);
}
else
term_putes(line->formatted + (lnb-1)*(CO-1) + PLNLEN(line), CO-1,
(line->attributes) ?
line->attributes + (lnb-1)*(CO-1) + PLNLEN(line) : NULL,
mask);
if (!get_flag(line->flags, line->dest, active->fnb))
term_underline_off();
}
/* sic_redowin: repaint the window from the lastlog */
void
sic_redowin(clear)
int clear;
{
struct log_ *tmpl;
if (clear)
{
term_clear();
refresh_status = 1;
}
else
{
int i;
for (i = 0; i < LI-2; i++)
{
term_move_cursor(0, i);
term_clreol();
}
}
active = current; /* for get_flag() */
if (tmpl = current->ldisp)
{
int i = LI-3;
while (i >= 0 && tmpl)
{
int j = LNCNT(tmpl);
if (get_flag(tmpl->flags, tmpl->dest, current->fnb)
|| get_option(Z_SHOWALL, tmpl->dest))
{
if (tmpl == current->ldisp && current->ltrunc)
j -= current->ltrunc;
while (i >= 0 && j--)
{
term_move_cursor(0, i--);
display_line(j, tmpl);
}
}
tmpl = tmpl->prevl;
}
}
}
/* sic_wptoggle: toggle W_NOPREFIX flag for current window */
void
sic_wptoggle()
{
if (option(current->wopt, W_NOPREFIX))
unset_option(current->wopt, W_NOPREFIX);
else
set_option(current->wopt, W_NOPREFIX);
}
/* sic_chgwin: switch current window
* -3 -> last, -2 -> previous, -1 -> next
*/
void
sic_chgwin(n)
int n;
{
struct window_ *w = wlist;
if (wlist->nextw == NULL)
{
sic_slog(LOG_CLIENT, "--- No other window!");
return;
}
if (n == -3)
{
if (oldcurrent)
{
w = current;
current = oldcurrent;
oldcurrent = w;
}
}
else
{
int i = current->ref, dir = 0;
if (n == -1)
dir = 1;
else if (n == -2)
dir = -1;
if (dir != 0)
{
i += dir;
while (i >= 0 && i <= win_max)
if (win_map[i])
break;
else
i += dir;
if (i < 0 || i > win_max)
{
i = (dir == -1) ? win_max : 0;
while (win_map[i] == NULL)
i += dir;
}
w = win_map[i];
}
else
if ((w = win_map[n]) == NULL)
return;
oldcurrent = current;
current = w;
}
sic_redowin(1);
}
/* sic_scroll: scroll the lastlog */
void
sic_scroll(n)
int n;
{
struct log_ *tmpl = current->ldisp;
active = current; /* for get_*() calls */
/* lame version which repaints all the window */
if (n > 0)
{
/* scrolling down */
if (current->ltrunc == 0 && current->ldisp == current->llog)
return;
if (current->ltrunc)
/* some lines are not displayed */
if (n >= current->ltrunc)
{
/* we are scrolling more than there are undisplayed lines */
n -= current->ltrunc;
current->ltrunc = 0;
}
else
/* there are more undisplayed lines than we are scrolling */
current->ltrunc -= n;
/* keep on scrolling */
while (n > 0 && tmpl)
if (tmpl = tmpl->nextl)
if (get_flag(tmpl->flags, tmpl->dest, current->fnb)
|| get_option(Z_SHOWALL, tmpl->dest))
n -= LNCNT(tmpl);
if (!tmpl)
tmpl = current->llog; /* bottom of the log */
if (n < 0)
/* went too much down: need to truncate the last log entry */
current->ltrunc = -n;
current->ldisp = tmpl;
}
else if (n < 0)
{
/* scrolling up */
int seen = LNCNT(current->ldisp) - current->ltrunc;
n *= -1;
if (current->ltrunc)
/* only some lines are displayed */
if (n >= seen)
{
/* we are scrolling more than there are displayed lines */
n -= seen;
current->ltrunc = 0;
tmpl = tmpl->prevl;
}
else
{
/* there are more displayed lines than we are scrolling */
current->ltrunc += n;
n = 0;
}
/* keep on scrolling */
while (n > 0 && tmpl)
{
while (tmpl && !(get_flag(tmpl->flags, tmpl->dest, current->fnb)
|| get_option(Z_SHOWALL, tmpl->dest)))
tmpl = tmpl->prevl;;
if (tmpl)
{
n -= LNCNT(tmpl);
tmpl = tmpl->prevl;
}
}
if (!tmpl)
{
tmpl = current->flog; /* top of the log */
n = 0; /* hack, this isn't exact */
}
if (n < 0)
{
/* went too much up */
tmpl = tmpl->nextl;
/* need to truncate the last log entry */
current->ltrunc = LNCNT(tmpl) + n;
}
current->ldisp = tmpl;
}
else
return;
sic_redowin(0); /* unefficient -hmmpf */
}
/* sic_scroll2:
* -1: top +1: bottom
* -2: previous keyword +2: next keyword
* if txt is non NULL, then search for txt instead of keywords.
*/
void
sic_scroll2(opt, txt)
signed char opt;
char *txt;
{
struct log_ *tmpl = current->ldisp;
int n = 0;
assert( !txt || (txt && (opt == -2 || opt == 0 || opt == 2)));
active = current;
if (current->searchstr)
{
free(current->searchstr);
current->searchstr = NULL;
}
if (txt)
current->searchstr = strdup(txt);
switch (opt)
{
case 0:
/* do nothing */
break;
case -1:
current->ldisp = current->flog;
current->ltrunc = 0;
sic_scroll(2*(LI-2)/3);
break;
case +1:
current->ldisp = current->llog;
current->ltrunc = 0;
sic_redowin(0);
break;
case -2:
while (tmpl)
{
while (tmpl && !(get_flag(tmpl->flags, tmpl->dest, current->fnb)
|| get_option(Z_SHOWALL, tmpl->dest)))
tmpl = tmpl->prevl;;
if (tmpl)
{
n -= LNCNT(tmpl);
if (n < -(LI-2)/3-1)
if (txt == NULL && tmpl->flags & LOG_KEYWORD)
break;
else if (txt && strcasestr(tmpl->formatted, txt))
break;
tmpl = tmpl->prevl;
}
}
if (tmpl)
sic_scroll(n + (LI-2)/3);
else
term_beep();
break;
case +2:
while (tmpl)
if (tmpl = tmpl->nextl)
if (get_flag(tmpl->flags, tmpl->dest, current->fnb)
|| get_option(Z_SHOWALL, tmpl->dest))
{
n += LNCNT(tmpl);
if (txt == NULL && tmpl->flags & LOG_KEYWORD)
break;
else if (txt && strcasestr(tmpl->formatted, txt))
break;
}
if (tmpl)
sic_scroll(n + (LI-2)/3);
else
term_beep();
break;
default:
abort(); /* never */
}
}
/*
* CHANNELS
*/
/* map_c2w: map a channel to a window
* returns 0 if name isn't a channel
* returns 1 it is the window's channel
* returns 2 if is not ...
*
* This doesn't indicate if the user speaking is actually on the channel or
* not... to be added.
*/
int
map_c2w(name)
char *name;
{
struct channel_ *ctmp;
if ((ctmp = get_channel(name)) && !strcasecmp(name, ctmp->chname))
if (ctmp->on == current)
return 1;
else
return 2;
else
return 0;
}
void
chg_channel(name)
char *name;
{
struct channel_ *ctmp = clist;
char *sname;
assert(name && *name);
while (name && ctmp)
{
if (option(ctmp->copt, C_JOINING) && *ctmp->chname == '!')
{
if (ctmp->chname[1] == '#' || ctmp->chname[1] == '!')
sname = ctmp->chname+1;
else
sname = ctmp->chname;
if (!strcasecmp(name+6, sname+1))
{
vsic_slog(LOG_DEBUG, "channel %s renamed to %s", ctmp->chname,
name);
*sname = '!';
cfg_read(sname);
free(ctmp->chname);
ctmp->chname = strdup(name);
name = NULL;
}
}
ctmp = ctmp->nextc;
}
assert(name == NULL);
}
/* new_channel: creates a new struct for channel data */
void
new_channel(name)
char *name;
{
struct channel_ *new, *save;
assert (map_c2w(name) == 0);
new = (struct channel_ *) malloc(sizeof(struct channel_));
bzero(new, sizeof(struct channel_));
new->nextc = clist;
clist = new;
new->on = current;
new->on->via = server;
/*bzero(new->copt, sizeof(new->copt));*/
new->chname = strdup(name);
new->chkey = NULL;
save = current->defchan;
current->defchan = new; /* so that cmd_option gets the right channel name */
cfg_read(name);
current->defchan = save;
if (!current->defchan)
current->defchan = new;
}
/* join_channel: try to join a channel */
void
join_channel(name, try)
char *name;
int try;
{
struct channel_ *ctmp;
ctmp = get_channel(name);
if (try == 0)
{
assert(ctmp);
if (option(ctmp->copt, C_JOINED))
{
vsic_slog(LOG_CLIENT, "--- Already on channel %s.", name);
return;
}
if (!option(server->sopt, S_CONNECTED))
{
sic_slog(LOG_CLIENT, "--- Not connected.");
return;
}
vsic_write("JOIN %s %s", name, (ctmp->chkey) ? ctmp->chkey : "");
vsic_write("LAME:%s", name); /* trick to know when JOIN was completed */
set_option(ctmp->copt, C_JOINING);
}
else if (ctmp)
unset_option(ctmp->copt, C_JOINING);
}
/* del_channel: delete channel structures if appropriate */
void
del_channel(name)
char *name;
{
struct channel_ *del;
if (name)
{
del = get_channel(name);
assert(del);
unset_option(del->copt, C_JOINED);
unset_option(del->copt, C_CHOP);
if (!option(del->copt, C_PART))
return;
need_collect++;
set_option(del->copt, C_DEL);
}
else
{
del = clist;
while (del)
{
if (del->on->via == server)
del_channel(del->chname);
del = del->nextc;
}
}
}
/* part_channel: mark channel for deletion */
void
part_channel(name, comment, forget)
char *name, *comment;
int forget;
{
struct channel_ *ctmp;
assert(comment);
ctmp = get_channel(name);
assert(ctmp);
if (option(ctmp->copt, C_JOINING))
{
sic_slog(LOG_CLIENT, "--- Join in progress, try later.");
return;
}
if (forget)
set_option(ctmp->copt, C_PART);
if (option(server->sopt, S_CONNECTED))
vsic_write("PART %s :%s", name, (comment) ? comment : "");
else if (forget)
{
del_channel(name);
collect_channel();
}
return;
}
/* rejoin_channel: set auto rejoin for channel */
void
rejoin_channel(name)
{
struct channel_ *ctmp = clist;
if (name)
{
ctmp = get_channel(name);
assert(ctmp);
if (!option(ctmp->copt, C_REJOIN))
{
need_collect++;
unset_option(ctmp->copt, C_REJOINED);
}
set_option(ctmp->copt, C_REJOIN);
}
else
while (ctmp)
{
if (ctmp->on->via == server)
rejoin_channel(ctmp->chname);
ctmp = ctmp->nextc;
}
}
void
add_member(channel, nick, rpl)
char *channel, *nick, rpl;
{
struct channel_ *ctmp;
struct member_ *mtmp;
assert(channel && *channel);
ctmp = get_channel(channel);
assert(ctmp);
if (nick)
{
if (!rpl || !option(ctmp->copt, C_NAMES))
{
mtmp = (struct member_ *) malloc(sizeof(struct member_));
mtmp->next = ctmp->members;
ctmp->members = mtmp;
mtmp->nick = strdup(nick);
if (++ctmp->member_cnt == 1)
{
unset_option(ctmp->copt, C_JOINING);
set_option(ctmp->copt, C_JOINED);
unset_option(ctmp->copt, C_REJOIN);
unset_option(ctmp->copt, C_REJOINED);
}
}
}
else
set_option(ctmp->copt, C_NAMES);
}
/* del_member: remove member(s) from channel(s)
* channel, nick: remove nickname from channel
* NULL, nick: remove nickname from all channels
* channel, NULL: remove everybody from channel, unset C_JOINING
* NULL, NULL: remove everybody from all channels, unset C_JOINING
*/
int
del_member(channel, nick)
char *channel, *nick;
{
struct channel_ *ctmp = clist;
if (channel == NULL)
if (nick == NULL)
{
while (ctmp)
{
if (ctmp->on->via == server)
del_member(ctmp->chname, NULL);
ctmp = ctmp->nextc;
}
return 0;
}
else
{
struct window_ *wtmp = wlist;
assert(nick && *nick);
while (wtmp)
{
unset_option(wtmp->wopt, W_MCAST);
wtmp = wtmp->nextw;
}
while (ctmp)
{
if (ctmp->on->via == server && del_member(ctmp->chname, nick))
set_option(ctmp->on->wopt, W_MCAST);
ctmp = ctmp->nextc;
}
return 0;
}
else
{
ctmp = get_channel(channel);
assert(ctmp);
if (nick)
{
struct member_ **mtmp = &(ctmp->members), *del;
while (*mtmp && strcasecmp((*mtmp)->nick, nick))
mtmp = &((*mtmp)->next);
if (*mtmp == NULL)
return 0;
del = *mtmp;
*mtmp = (*mtmp)->next;
free(del->nick);
free(del);
ctmp->member_cnt--;
return 1;
}
else
{
struct member_ *mtmp = ctmp->members, *del;
while (del = mtmp)
{
mtmp = mtmp->next;
free(del->nick);
free(del);
}
ctmp->members = NULL;
ctmp->member_cnt = 0;
unset_option(ctmp->copt, C_CHOP);
unset_option(ctmp->copt, C_NAMES);
unset_option(ctmp->copt, C_JOINING);
unset_option(ctmp->copt, C_JOINED);
return 0;
}
}
}
void
chg_member(old, new)
char *old, *new;
{
struct channel_ *ctmp = clist;
assert(old && *old && new && *new);
while (ctmp)
{
struct member_ *mtmp = ctmp->members;
unset_option(ctmp->on->wopt, W_MCAST);
if (ctmp->on->via == server && option(ctmp->copt, C_JOINED))
{
while (mtmp && strcasecmp(mtmp->nick, old))
mtmp = mtmp->next;
if (mtmp)
{
free(mtmp->nick);
mtmp->nick = strdup(new);
set_option(ctmp->on->wopt, W_MCAST);
}
}
ctmp = ctmp->nextc;
}
}
int
is_member(channel, nick)
char *channel, *nick;
{
struct channel_ *ctmp;
struct member_ *mtmp;
assert(channel && *channel && nick && *nick);
if (!(ctmp = get_channel(channel)))
return 0;
mtmp = ctmp->members;
while (mtmp && strcasecmp(mtmp->nick, nick))
mtmp = mtmp->next;
if (mtmp)
return 1;
else
return 0;
}
/* collect_channel: auto rejoins / deletions */
void
collect_channel()
{
struct channel_ *ctmp = clist;
while (ctmp)
{
if (ctmp->on->via != server)
{
ctmp = ctmp->nextc;
continue;
}
if (option(ctmp->copt, C_REJOIN) && option(server->sopt, S_CONNECTED))
if (!option(ctmp->copt, C_REJOINED))
{
vsic_slog(LOG_CLIENT, "--- Rejoining %s.", ctmp->chname);
join_channel(ctmp->chname, 0);
set_option(ctmp->copt, C_REJOINED);
ctmp->rejoin = time(NULL);
ctmp = ctmp->nextc;
continue;
}
else if (time(NULL) - ctmp->rejoin > 600)
{
unset_option(ctmp->copt, C_REJOINED);
continue;
}
if (option(ctmp->copt, C_DEL))
{
struct channel_ *del = ctmp, **dtmp = &clist;
struct window_ *w;
ctmp = ctmp->nextc;
vsic_slog(LOG_CLIENT, "Forgetting about channel %s.", del->chname);
del_member(del->chname, NULL);
assert(del);
while (*dtmp != del)
dtmp = &((*dtmp)->nextc);
assert(*dtmp);
*dtmp = del->nextc;
free(del->chname);
if (del->chkey)
free(del->chkey);
if ((w = del->on)->defchan == del)
{
struct channel_ *ctmp2 = clist;
while (ctmp2)
{
if (ctmp2->on == w)
break;
ctmp2 = ctmp2->nextc;
}
w->defchan = ctmp2;
}
opt_free(&(del->custc));
free(del);
need_collect--;
continue;
}
ctmp = ctmp->nextc;
}
}
/* default_channel: changes default channel or query */
void
default_channel(name)
char *name;
{
struct channel_ *ctmp;
if (ctmp = get_channel(name))
{
assert(ctmp->on == current);
current->defchan = ctmp;
}
else
{
struct window_ *wtmp = wlist;
if (*name)
while (wtmp)
{
if (wtmp->via == server && !strcasecmp(wtmp->query, name))
wtmp->query[0] = '\0';
wtmp = wtmp->nextw;
}
strcpy(current->query, name);
}
}
/* get_query: returns the nick being queried if any */
char *
get_query()
{
if (*current->query)
return current->query;
else
return NULL;
}
/* select_query: selects a new active window in case of /query */
void
select_query(dest)
char *dest;
{
struct window_ *w = wlist;
while (w)
{
if (((server == w->via) || (option(server->sopt, S_DCC)))
&& !strcasecmp(dest, w->query))
{
active = w;
break;
}
w = w->nextw;
}
}
/* select_active: select active window (where things will be displayed)
* if dest isn't null, the channel is looked up to decide.
* if dest is NULL,
* action == 2 -> current
* action == 1 -> current if same server, default otherwise
* otherwise, default window is chosen (on a server base)
*/
void
select_active(dest, action)
char *dest;
int action;
{
if (dest)
{
struct channel_ *ctmp = clist;
ctmp = get_channel(dest);
assert(ctmp);
active = ctmp->on;
}
else
{
struct window_ *w = wlist;
switch (action)
{
case 2:
active = current;
break;
case 1:
active = current;
if (current->via == server)
break;
/* fall through */
default:
while (w && (!option(w->wopt, W_DEFAULT) || server != w->via)
&& (!option(w->wopt, W_DCC) || !option(server->sopt, S_DCC)))
w = w->nextw;
assert(w);
active = w;
break;
}
}
}
/* function written before the generic ones, should be removed later */
void
set_chop(channel, op)
char *channel;
int op;
{
struct channel_ *ctmp;
ctmp = get_channel(channel);
if (ctmp)
if (op)
{
if (op == 1 || (op == 2 && ctmp->members == NULL))
set_option(ctmp->copt, C_CHOP);
}
else
unset_option(ctmp->copt, C_CHOP);
}
syntax highlighted by Code2HTML, v. 0.9.1