#ident "@(#)lastlog.c 1.5"
/*
* lastlog.c: handles the lastlog features of irc.
*
* Written By Michael Sandrof
*
* Copyright(c) 1990
*
* See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "irc.h"
#include "lastlog.h"
#include "window.h"
#include "screen.h"
#include "vars.h"
#include "ircaux.h"
#include "output.h"
#include "misc.h"
#include "hook.h"
#include "status.h"
#include "fset.h"
#include "tcommand.h"
/*
* lastlog_level: current bitmap setting of which things should be stored in
* the lastlog. The LOG_MSG, LOG_NOTICE, etc., defines tell more about this
*/
static unsigned long lastlog_level;
static unsigned long notify_level;
static unsigned long msglog_level = 0;
unsigned long beep_on_level = 0;
FILE *logptr = NULL;
/*
* msg_level: the mask for the current message level. What? Did he really
* say that? This is set in the set_lastlog_msg_level() routine as it
* compared to the lastlog_level variable to see if what ever is being added
* should actually be added
*/
static unsigned long msg_level = LOG_CRAP;
static char *levels[] = {
"CRAP", "PUBLIC", "MSGS", "NOTICES",
"WALLS", "WALLOPS", "NOTES", "OPNOTES",
"SNOTES", "ACTIONS", "DCC", "CTCP",
"USERLOG1", "USERLOG2", "USERLOG3", "USERLOG4",
"USERLOG5", "BEEP", "SEND_MSG", "KILL"
};
#define NUMBER_OF_LEVELS (sizeof(levels) / sizeof(char *))
/* set_lastlog_msg_level: sets the message level for recording in the lastlog */
unsigned long set_lastlog_msg_level(unsigned long level)
{
unsigned long old;
old = msg_level;
msg_level = level;
return (old);
}
/*
* bits_to_lastlog_level: converts the bitmap of lastlog levels into a nice
* string format. Note that this uses the global buffer, so watch out
*/
char *bits_to_lastlog_level(unsigned long level)
{
static char buffer[281]; /* this *should* be enough for this */
int i;
unsigned long p;
if (level == LOG_ALL)
strcpy(buffer, "ALL");
else if (level == 0)
strcpy(buffer, "NONE");
else {
*buffer = '\0';
for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1) {
if (level & p) {
strmcat(buffer, levels[i], 280);
strmcat(buffer, " ", 280);
}
}
}
return (buffer);
}
unsigned long parse_lastlog_level(char *str)
{
char *ptr, *rest;
int len, i;
unsigned long p, level;
int neg;
level = 0;
while ((str = next_arg(str, &rest)) != NULL) {
while (str) {
if ((ptr = strchr(str, ',')) != NULL)
*ptr++ = '\0';
if ((len = strlen(str)) != 0) {
if (my_strnicmp(str, "ALL", len) == 0)
level = LOG_ALL;
else if (my_strnicmp(str, "NONE", len) == 0)
level = 0;
else {
if (*str == '-') {
str++;
len--;
neg = 1;
} else
neg = 0;
for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1) {
if (!my_strnicmp(str, levels[i], len)) {
if (neg)
level &= (LOG_ALL ^ p);
else
level |= p;
break;
}
}
if (i == NUMBER_OF_LEVELS)
say("Unknown lastlog level: %s", str);
}
}
str = ptr;
}
str = rest;
}
return (level);
}
/*
* set_lastlog_level: called whenever a "SET LASTLOG_LEVEL" is done. It
* parses the settings and sets the lastlog_level variable appropriately. It
* also rewrites the LASTLOG_LEVEL variable to make it look nice
*/
void set_lastlog_level(Window * win, char *str, int unused)
{
lastlog_level = parse_lastlog_level(str);
set_string_var(LASTLOG_LEVEL_VAR, bits_to_lastlog_level(lastlog_level));
curr_scr_win->lastlog_level = lastlog_level;
}
/*
* set_msglog_level: called whenever a "SET MSGLOG_LEVEL" is done. It
* parses the settings and sets the msglog_level variable appropriately. It
* also rewrites the MSGLOG_LEVEL variable to make it look nice
*/
void set_msglog_level(Window * win, char *str, int unused)
{
msglog_level = parse_lastlog_level(str);
set_string_var(MSGLOG_LEVEL_VAR, bits_to_lastlog_level(msglog_level));
}
void remove_from_lastlog(Window * window)
{
Lastlog *tmp, *end_holder;
if (window->lastlog_tail) {
end_holder = window->lastlog_tail;
tmp = window->lastlog_tail->prev;
window->lastlog_tail = tmp;
if (tmp)
tmp->next = NULL;
else
window->lastlog_head = window->lastlog_tail;
window->lastlog_size--;
new_free(&end_holder->msg);
new_free((char **) &end_holder);
} else
window->lastlog_size = 0;
}
/*
* set_lastlog_size: sets up a lastlog buffer of size given. If the lastlog
* has gotten larger than it was before, all previous lastlog entry remain.
* If it get smaller, some are deleted from the end.
*/
void set_lastlog_size(Window * win_unused, char *unused, int size)
{
int i, diff;
Window *win = NULL;
while (traverse_all_windows(&win)) {
if (win->lastlog_size > size) {
diff = win->lastlog_size - size;
for (i = 0; i < diff; i++)
remove_from_lastlog(win);
}
win->lastlog_max = size;
}
}
/*
* lastlog: the /LASTLOG command. Displays the lastlog to the screen. If
* args contains a valid integer, only that many lastlog entries are shown
* (if the value is less than lastlog_size), otherwise the entire lastlog is
* displayed
*/
void cmd_lastlog(struct command *cmd, char *args)
{
int cnt, from = 0, p, i, level = 0, my_msg_level, len, mask = 0, header = 1, lines = 0;
Lastlog *start_pos;
char *the_match = NULL, *arg;
char *blah = NULL;
FILE *fp = NULL;
message_from(NULL, LOG_CURRENT);
cnt = curr_scr_win->lastlog_size;
while ((arg = new_next_arg(args, &args)) != NULL) {
if (*arg == '-') {
arg++;
if (!(len = strlen(arg))) {
header = 0;
continue;
} else if (!my_strnicmp(arg, "MAX", len)) {
char *ptr = NULL;
ptr = new_next_arg(args, &args);
if (ptr)
lines = atoi(ptr);
if (lines < 0)
lines = 0;
} else if (!my_strnicmp(arg, "LITERAL", len)) {
if (the_match) {
say("Second -LITERAL argument ignored");
(void) new_next_arg(args, &args);
continue;
}
if ((the_match = new_next_arg(args, &args)) != NULL)
continue;
say("Need pattern for -LITERAL");
return;
} else if (!my_strnicmp(arg, "BEEP", len)) {
if (the_match) {
say("-BEEP is exclusive; ignored");
continue;
} else
the_match = "\007";
}
#if 0
else if (!my_strnicmp(arg, "CLEAR", len)) {
free_lastlog(curr_scr_win);
say("Cleared lastlog");
return;
}
#endif
else if (!my_strnicmp(arg, "FILE", len)) {
if (args && *args) {
char *filename;
filename = next_arg(args, &args);
if (!(fp = fopen(filename, "w"))) {
bitchsay("cannot open file %s", filename);
return;
}
} else {
bitchsay("Filename needed for save");
return;
}
} else {
for (i = 0, p = 1; i < NUMBER_OF_LEVELS; i++, p <<= 1) {
if (my_strnicmp(levels[i], arg, len) == 0) {
mask |= p;
break;
}
}
if (i == NUMBER_OF_LEVELS) {
bitchsay("Unknown flag: %s", arg);
message_from(NULL, LOG_CRAP);
return;
}
}
} else {
if (level == 0) {
if (the_match || isdigit(*arg)) {
cnt = atoi(arg);
level++;
} else
the_match = arg;
} else if (level == 1) {
from = atoi(arg);
level++;
}
}
}
start_pos = curr_scr_win->lastlog_head;
for (i = 0; (i < from) && start_pos; start_pos = start_pos->next)
if (!mask || (mask & start_pos->level))
i++;
for (i = 0; (i < cnt) && start_pos; start_pos = start_pos->next)
if (!mask || (mask & start_pos->level))
i++;
level = curr_scr_win->lastlog_level;
my_msg_level = set_lastlog_msg_level(0);
if (start_pos == NULL)
start_pos = curr_scr_win->lastlog_tail;
else
start_pos = start_pos->prev;
/* Let's not get confused here, display a seperator.. -lynx */
strip_ansi_in_echo = 0;
if (header && !fp)
say("Lastlog:");
if (the_match) {
blah = (char *) alloca(strlen(the_match) + 4);
sprintf(blah, "*%s*", the_match);
}
for (i = 0; (i < cnt) && start_pos; start_pos = start_pos->prev) {
if (!mask || (mask & start_pos->level)) {
i++;
if (!the_match || wild_match(blah, start_pos->msg)) {
if (!fp) {
put_it("%s", !get_int_var(LASTLOG_ANSI_VAR) ? stripansicodes(start_pos->msg) : start_pos->msg);
} else
fprintf(fp, "%s\n", !get_int_var(LASTLOG_ANSI_VAR) ? stripansicodes(start_pos->msg) : start_pos->msg);
if (lines == 0)
continue;
else if (lines == 1)
break;
lines--;
}
}
}
if (header && !fp)
say("End of Lastlog");
strip_ansi_in_echo = 1;
curr_scr_win->lastlog_level = level;
message_from(NULL, LOG_CRAP);
set_lastlog_msg_level(my_msg_level);
}
/*
* add_to_lastlog: adds the line to the lastlog. If the LASTLOG_CONVERSATION
* variable is on, then only those lines that are user messages (private
* messages, channel messages, wall's, and any outgoing messages) are
* recorded, otherwise, everything is recorded
*/
void add_to_lastlog(Window * window, const char *line)
{
Lastlog *new;
if (window == NULL)
window = curr_scr_win;
if (window->lastlog_level & msg_level) {
/* no nulls or empty lines (they contain "> ") */
if (line && (strlen(line) > 2)) {
new = (Lastlog *) new_malloc(sizeof(Lastlog));
new->next = window->lastlog_head;
new->prev = NULL;
new->level = msg_level;
new->msg = NULL;
new->time = time(NULL);
malloc_strcpy(&(new->msg), line);
if (window->lastlog_head)
window->lastlog_head->prev = new;
window->lastlog_head = new;
if (window->lastlog_tail == NULL)
window->lastlog_tail = window->lastlog_head;
if (window->lastlog_size++ >= window->lastlog_max)
remove_from_lastlog(window);
}
}
}
unsigned long real_notify_level(void)
{
return (notify_level);
}
unsigned long real_lastlog_level(void)
{
return (lastlog_level);
}
void set_notify_level(Window * win, char *str, int unused)
{
notify_level = parse_lastlog_level(str);
set_string_var(NOTIFY_LEVEL_VAR, bits_to_lastlog_level(notify_level));
curr_scr_win->notify_level = notify_level;
}
int logmsg(unsigned long log_type, char *from, char *string, int flag)
{
char *timestr;
time_t t;
char *filename = NULL;
char *expand = NULL;
char *type = NULL;
char **lines = NULL;
if (!get_string_var(MSGLOGFILE_VAR))
return 0;
t = time(NULL);
timestr = update_clock(GET_TIME);
switch (flag) {
case 0:
if (!(type = bits_to_lastlog_level(log_type)))
type = "Unknown";
if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, type, from, string ? string : ""))
break;
if (!logptr)
return 0;
if (msglog_level & log_type) {
lines =
split_up_line(stripansicodes
(convert_output_format
(get_fset_var(FORMAT_MSGLOG_FSET) ? get_fset_var(FORMAT_MSGLOG_FSET) : "[$[10]0] [$1] - $2-",
"%s %s %s %s", type, timestr, from, string)));
for (; *lines; lines++)
fprintf(logptr, "%s\n", *lines);
fflush(logptr);
}
break;
case 1:
malloc_sprintf(&filename, "%s", get_string_var(MSGLOGFILE_VAR));
expand = expand_twiddle(filename);
new_free(&filename);
if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, "On", expand, "")) {
new_free(&expand);
return 1;
}
if (logptr) {
new_free(&expand);
return 1;
}
if (!(logptr = fopen(expand, get_int_var(APPEND_LOG_VAR) ? "at" : "wt"))) {
set_int_var(MSGLOG_VAR, 0);
new_free(&expand);
return 0;
}
fprintf(logptr, "MsgLog started [%s]\n", my_ctime(t));
fflush(logptr);
if (string) {
int i;
i = logmsg(LOG_CURRENT, from, string, 0);
return i;
}
bitchsay("Now logging messages to: %s", expand);
new_free(&expand);
break;
case 2:
if (!do_hook(MSGLOG_LIST, "%s %s %s %s", timestr, "Off", "", ""))
return 1;
if (!logptr)
return 1;
fprintf(logptr, "MsgLog ended [%s]\n", my_ctime(t));
fclose(logptr);
logptr = NULL;
break;
case 3:
return logptr ? 1 : 0;
break;
case 4:
if (!logptr)
return 1;
fprintf(logptr, "[TimeStamp %s]\n", my_ctime(t));
fflush(logptr);
break;
default:
bitchsay("Bad Flag passed to logmsg");
return 0;
}
return 1;
}
void set_beep_on_msg(Window * win, char *str, int unused)
{
beep_on_level = parse_lastlog_level(str);
set_string_var(BEEP_ON_MSG_VAR, bits_to_lastlog_level(beep_on_level));
}
void cmd_awaylog(struct command *cmd, char *args)
{
if (args && *args) {
msglog_level = parse_lastlog_level(args);
set_string_var(MSGLOG_LEVEL_VAR, bits_to_lastlog_level(msglog_level));
put_it("%s", convert_output_format("$G Away logging set to: $0-", "%s", get_string_var(MSGLOG_LEVEL_VAR)));
} else
put_it("%s", convert_output_format("$G Away logging currently: $0-", "%s", bits_to_lastlog_level(msglog_level)));
}
syntax highlighted by Code2HTML, v. 0.9.1