/* * 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, "", "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 : ""); /*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 : "", 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 : "", 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 : "", (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); }