/*
* option.c: getting, setting options
*
* Copyright(c) 1997-2000 - All Rights Reserved
*
* See the COPYRIGHT file.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: option.c,v 1.36 2000/07/31 22:33:55 kalt Exp $";
#endif
#include "os.h"
#include "struct.h"
#include "window.h"
#include "format.h"
#include "option.h"
#include "utils.h"
#include "config.h"
#include "counter.h"
extern struct window_ *current, *active;
extern struct server_ *server;
static const struct custom_ defcust =
{
{ 0 }, { 0 },
default_format_str, NULL,
{
LOG_DEBUG|LOG_ISNIF,
LOG_DEFAULT,
LOG_DEFAULT,
LOG_DEFAULT,
LOG_HIGHLIGHT|LOG_KEYWORD,
0, 0, 0,
LOG_ALL|LOG_IGNORE|LOG_HIDE,
LOG_INPUT|LOG_OSNIF, 0
},
{
0,
LOG_HIDE|LOG_IGNORE,
LOG_IGNORE,
0, 0, 0, 0, 0, 0, 0, 0
},
NULL, NULL, (char) 0
};
static struct custom_ topcust;
static struct custom_ protocust[P_MAX];
void
opt_init()
{
bzero(&topcust, sizeof(topcust));
bzero(&protocust, sizeof(protocust));
}
void
opt_cfg()
{
struct server_ dummy, *save;
cfg_read("top");
save = server; server = &dummy;
dummy.protocol = P_IRC; cfg_read("irc");
dummy.protocol = P_E; cfg_read("EFnet");
dummy.protocol = P_U; cfg_read("Undernet");
dummy.protocol = P_D; cfg_read("DALnet");
server = save;
}
void
opt_free(cust)
struct custom_ *cust;
{
struct keyword_ *ktmp = cust->keywords, *kdel;
struct rewrite_ *rtmp = cust->rules, *rdel;
int i;
if (cust->format)
{
for (i = 0; i <= F_MAX; i++)
if (cust->format[i])
free(cust->format[i]);
free(cust->format);
}
if (cust->numerics)
{
for (i = 0; i <= N_MAX; i++)
if (cust->numerics[i])
free(cust->numerics[i]);
free(cust->numerics);
}
while (kdel = ktmp)
{
ktmp = ktmp->nextk;
free(kdel->kword);
#if defined(HAVE_REGEXP)
if (kdel->kflag & K_REGEXP)
regfree(&(kdel->rword));
#endif
free(kdel);
}
while (rdel = rtmp)
{
rtmp = rtmp->nextr;
free(rdel->match);
free(rdel->new);
#if defined(HAVE_REGEXP)
regfree(&(rdel->preg));
#endif
free(rdel);
}
if (cust->log)
fclose(cust->log);
}
/* sic_logfile: log the specific line to a file, unless filtered */
void
sic_logfile(line)
struct log_ *line;
{
struct custom_ *all_list[5] = { &topcust, NULL, NULL, NULL, NULL};
int i;
if (server)
{
if (server->protocol)
all_list[1] = &protocust[server->protocol-1];
all_list[2] = &(server->custs);
}
all_list[3] = &(active->custw);
if (line->dest && get_channel(line->dest))
all_list[4] = &(get_channel(line->dest)->custc);
for(i = 4; i >= 0; i--)
{
if (all_list[i] == NULL)
continue;
if (all_list[i]->logfilter == 0)
continue;
if (all_list[i]->log &&
get_flag(line->flags, line->dest, all_list[i]->logfilter-1))
{
fprintf(all_list[i]->log, "%s%s\n", line->prefix,
line->formatted);
fflush(all_list[i]->log);
}
}
}
/*
* GET - easy part
*/
/* get_format: returns the format string for a particular channel/window */
char *
get_format(type, channel)
unsigned int type;
char *channel;
{
struct channel_ *ctmp;
assert (type >= 0 && type <= F_MAX);
if (ctmp = get_channel(channel))
if (ctmp->custc.format && ctmp->custc.format[type])
return ctmp->custc.format[type];
if (active->custw.format && active->custw.format[type])
return active->custw.format[type];
if (server)
{
if (server->custs.format && server->custs.format[type])
return server->custs.format[type];
if (server->protocol && protocust[server->protocol-1].format
&& protocust[server->protocol-1].format[type])
return server->custs.format[type];
}
if (topcust.format && topcust.format[type])
return topcust.format[type];
return defcust.format[type];
}
/* get_nformat: returns the format string for a particular channel/window */
char *
get_nformat(fnum, channel)
unsigned int fnum;
char *channel;
{
struct channel_ *ctmp;
assert (fnum >= 0 && fnum <= N_MAX);
if (ctmp = get_channel(channel))
if (ctmp->custc.numerics && ctmp->custc.numerics[fnum])
return ctmp->custc.numerics[fnum];
if (active->custw.numerics && active->custw.numerics[fnum])
return active->custw.numerics[fnum];
if (server)
{
if (server->custs.numerics && server->custs.numerics[fnum])
return server->custs.numerics[fnum];
if (server->protocol && protocust[server->protocol-1].numerics
&& protocust[server->protocol-1].numerics[fnum])
return server->custs.numerics[fnum];
}
if (topcust.numerics && topcust.numerics[fnum])
return topcust.numerics[fnum];
/* if (defcust.numerics[fnum])
return defcust.numerics[fnum]; */
return get_format(F_NUM, channel);
}
/* get_option: get the option setting for a particular channel/window */
int
get_option(opt, channel)
unsigned int opt;
char *channel;
{
struct channel_ *ctmp;
if (ctmp = get_channel(channel))
{
if (option(ctmp->custc.zopt_on, opt))
return 1;
if (option(ctmp->custc.zopt_off, opt))
return 0;
}
if (option(active->custw.zopt_on, opt))
return 1;
if (option(active->custw.zopt_off, opt))
return 0;
if (server)
{
if (option(server->custs.zopt_on, opt))
return 1;
if (option(server->custs.zopt_off, opt))
return 0;
if (server->protocol)
{
if (option(protocust[server->protocol-1].zopt_on, opt))
return 1;
if (option(protocust[server->protocol-1].zopt_off, opt))
return 0;
}
}
if (option(topcust.zopt_on, opt))
return 1;
if (option(topcust.zopt_off, opt))
return 0;
if (option(defcust.zopt_on, opt))
return 1;
if (option(defcust.zopt_off, opt)) /* useless */
return 0;
return 0;
}
/* get_flag: get the flag setting for a particular channel/window */
int
get_flag(mask, channel, nb)
unsigned int mask, nb;
char *channel;
{
struct channel_ *ctmp;
int rc = -1;
assert (nb >= 0 && nb < 11);
if (ctmp = get_channel(channel))
{
if (ctmp->custc.mask_off[nb] & mask)
rc = 0;
else if (ctmp->custc.mask_on[nb] & mask)
rc = 1;
else
rc = -1;
}
if (rc == -1)
if (active->custw.mask_off[nb] & mask)
rc = 0;
else if (active->custw.mask_on[nb] & mask)
rc = 1;
else if (server && server->custs.mask_off[nb] & mask)
rc = 0;
else if (server && server->custs.mask_on[nb] & mask)
rc = 1;
else if (server && server->protocol &&
protocust[server->protocol-1].mask_off[nb] & mask)
rc = 0;
else if (server && server->protocol &&
protocust[server->protocol-1].mask_on[nb] & mask)
rc = 1;
else if (topcust.mask_off[nb] & mask)
rc = 0;
else if (topcust.mask_on[nb] & mask)
rc = 1;
else if (defcust.mask_off[nb] & mask)
rc = 0;
else if (defcust.mask_on[nb] & mask)
rc = 1;
else
rc = 0;
if (mask & LOG_DEL)
if (rc != get_flag(mask ^ LOG_DEL, channel, nb))
/* LOG_DEL made a difference, so return rc (overrides Z_UNDEL) */
return rc;
else if (rc == 1)
return get_option(Z_UNDEL, channel);
return rc;
}
int
get_keyword(channel, where, attr, flags)
char *channel, **where, *attr;
unsigned int *flags;
{
struct keyword_ *ktmp = NULL;
char *cp, *best = NULL, bestv = 0;
int i, len = 0;
#if defined(HAVE_REGEXP)
regmatch_t pmatch[20];
#endif
assert(channel);
*attr = 0;
for (i = 0 ; i < 5 ; i++)
{
switch (i)
{
case 0:
ktmp = get_channel(channel)->custc.keywords;
break;
case 1:
ktmp = active->custw.keywords;
break;
case 2:
if (server)
ktmp = server->custs.keywords;
break;
case 3:
if (server && server->protocol)
ktmp = protocust[server->protocol-1].keywords;
break;
case 4:
ktmp = topcust.keywords;
break;
default:
abort();
}
while (ktmp)
{
if ((((ktmp->kflag & K_REGEXP) == 0)
&& (cp = strcasestr(*where, ktmp->kword)))
#if defined(HAVE_REGEXP)
|| (ktmp->kflag & K_REGEXP &&
regexec(&(ktmp->rword), *where, 20, pmatch, 0) == 0)
#endif
)
{
*flags |= LOG_KEYWORD;
if (ktmp->kflag & K_BEEP)
*attr |= K_BEEP;
if (ktmp->kflag & K_SPACE)
*attr |= K_SPACE;
if (ktmp->kflag & K_NOTIFY)
set_option(active->wopt, W_KEYWORD);
if (ktmp->kflag & K_HIDE)
*flags |= LOG_HIDE;
if (ktmp->kflag & K_IGNORE)
*flags |= LOG_IGNORE;
#if defined(HAVE_REGEXP)
if (ktmp->kflag & K_REGEXP)
cp = *where + pmatch[0].rm_so;
#endif
if (best == NULL || cp < best
&& (ktmp->kflag & (K_BOLD|K_UNDERLINE|K_STANDOUT|K_CHIDE)))
{
best = cp;
bestv = ktmp->kflag & (K_BOLD|K_UNDERLINE|K_STANDOUT|K_CHIDE);
#if defined(HAVE_REGEXP)
if (ktmp->kflag & K_REGEXP)
len = pmatch[0].rm_eo - pmatch[0].rm_so;
else
#endif
len = strlen(ktmp->kword);
}
}
ktmp = ktmp->nextk;
}
}
if (best)
{
*where = best;
*attr |= bestv;
}
else
*where = NULL;
return len;
}
void
get_ignore(from, channel, flags)
char *from, *channel;
unsigned int *flags;
{
struct keyword_ *ktmp = NULL;
int i;
for (i = (channel) ? 0 : 1 ; i < 5 ; i++)
{
switch (i)
{
case 0:
ktmp = get_channel(channel)->custc.keywords;
break;
case 1:
ktmp = active->custw.keywords;
break;
case 2:
if (server)
ktmp = server->custs.keywords;
break;
case 3:
if (server && server->protocol)
ktmp = protocust[server->protocol-1].keywords;
break;
case 4:
ktmp = topcust.keywords;
break;
default:
abort();
}
while (ktmp)
{
if (ktmp->kflag & (K_OIGNORE|K_OHIGHLIGHT))
if (rmatch(ktmp->kword, from))
{
if (ktmp->kflag & K_OIGNORE)
*flags |= LOG_IGNORE;
if (ktmp->kflag & K_OHIGHLIGHT)
*flags |= LOG_HIGHLIGHT;
}
ktmp = ktmp->nextk;
}
}
}
/* get_rewrite: rewrite the output, if applicable. Also deal with counters */
int
get_rewrite(fmt, string, attributes, flags, channel)
int fmt;
char *string, *channel;
u_char *attributes;
unsigned int *flags;
{
#if defined(HAVE_REGEXP)
struct rewrite_ *rtmp = NULL;
int i, rc = 0;
regmatch_t pmatch[20];
for (i = (channel) ? 0 : 1 ; i < 5 ; i++)
{
switch (i)
{
case 0:
rtmp = get_channel(channel)->custc.rules;
break;
case 1:
rtmp = active->custw.rules;
break;
case 2:
if (server)
rtmp = server->custs.rules;
break;
case 3:
if (server && server->protocol)
rtmp = protocust[server->protocol-1].rules;
break;
case 4:
rtmp = topcust.rules;
break;
default:
abort();
}
while (rtmp)
{
if (rtmp->fmt == fmt
&& regexec(&(rtmp->preg), string, 20, pmatch, 0) == 0)
{
if (*rtmp->new)
{
char ostr[1024], oattr[1024],
*nfmt = rtmp->new, *nstr = string;
u_char *nattr =attributes;
strcpy(ostr, string);
bcopy(attributes, oattr, strlen(string)+1);
if (*nfmt == ':')
{
nfmt += 1;
*flags |= LOG_HIDE;
}
while (*nfmt)
{
if (*nfmt != '$')
{
*nstr++ = *nfmt++;
*nattr++ = '\0';
}
else
{
int n = -1, j;
if (*++nfmt == '{')
{
n = atoi(++nfmt);
nfmt = index(nfmt, '}') + 1;
}
else
{
n = atoi(nfmt);
while (isdigit((int) *nfmt))
nfmt++;
}
if (n < 20 && (j = pmatch[n].rm_so) != -1)
{
while (j < pmatch[n].rm_eo && ostr[j])
{
*nstr++ = ostr[j];
*nattr++ = oattr[j++];
}
}
}
}
*nstr = '\0';
*nattr = '\0';
}
else
rc += counter_add((void *)rtmp, string, attributes, pmatch,
flags, channel);
if (rtmp->type == 0)
return rc;
}
rtmp = rtmp->nextr;
}
}
return rc;
#endif
}
/*
* SET
*/
/* real_set:
*
*/
static void
real_set(action, cust, fnum, val_str, val_ui)
int fnum;
unsigned int action, *val_ui;
char **val_str;
struct custom_ *cust;
{
if (fnum >= 0)
{
char ***formats = &(cust->format);
int sz = F_MAX;
if (fnum > F_MAX)
{
fnum -= F_MAX+1;
sz = N_MAX;
formats= &(cust->numerics);
}
switch (action & O_ACTIONMASK)
{
case O_CLEAR:
if (*formats)
{
if ((*formats)[fnum])
free((*formats)[fnum]);
(*formats)[fnum] = NULL;
}
break;
case O_SET:
if ((*formats) == NULL)
{
*formats = (char **) malloc(sizeof(char *) * (sz+1));
bzero(*formats, sizeof(char *) * (sz+1));
}
if ((*formats)[fnum])
free((*formats)[fnum]);
(*formats)[fnum] = strdup(*val_str);
if (**val_str == ':')
*((*formats)[fnum]) = '\0';
break;
case O_GET:
if (*formats)
*val_str = (*formats)[fnum];
else
*val_str = NULL;
break;
}
}
else if (action & O_KEYWORD)
{
struct keyword_ *k1, *k2;
switch (action & O_ACTIONMASK)
{
case O_CLEAR:
k2 = cust->keywords;
while (k1 = k2)
{
k2 = k2->nextk;
#if defined(HAVE_REGEXP)
if (k1->kflag & K_REGEXP)
regfree(&(k1->rword));
#endif
free(k1);
}
cust->keywords = NULL;
break;
case O_SET:
k2 = cust->keywords;
while (k2 && strcasecmp(k2->kword, *val_str))
k2 = k2->nextk;
if (k2)
k2->kflag = *val_ui | (k2->kflag & K_REGEXP);
else
{
k1 = (struct keyword_ *) malloc(sizeof(struct keyword_));
#if defined(HAVE_REGEXP)
if (*val_ui & K_REGEXP)
{
static char rx_errbuf[1024];
int rx_err;
if (rx_err = regcomp(&(k1->rword), *val_str,
REG_EXTENDED|REG_ICASE))
{
if (regerror(rx_err, &(k1->rword), rx_errbuf, 1024)>1024)
*val_str="Error compiling regexp (rx_errbuf too small!)";
else
*val_str = rx_errbuf;
free(k1);
return;
}
}
#endif
k1->nextk = cust->keywords;
cust->keywords = k1;
k1->kword = strdup(*val_str);
k1->kflag = *val_ui;
}
*val_str = NULL;
break;
case O_GET:
k2 = cust->keywords;
if (*val_str)
while (k2 && strcasecmp(k2->kword, *val_str))
k2 = k2->nextk;
else
while (k2 && ++fnum)
k2 = k2->nextk;
if (k2)
{
*val_str = k2->kword;
*val_ui = (unsigned int) k2->kflag;
}
else
{
*val_str = NULL;
*val_ui = 0;
}
break;
}
}
else if (action & O_LOGFILE)
{
switch (action & O_ACTIONMASK)
{
case O_CLEAR:
*val_str = NULL;
if (cust->log)
{
fprintf(cust->log, "--- End of log\n");
fclose(cust->log);
cust->log = NULL;
}
break;
case O_GET:
*val_ui = cust->logfilter-1;
*val_str = (cust->log) ? "Active" : "Disabled";
break;
case O_SET:
cust->logfilter = *val_ui+1;
if (**val_str == '\0')
{
if (cust->log)
*val_str = NULL;
else
*val_str = "No active log (filter set)";
return;
}
if (cust->log && **val_str)
{
fprintf(cust->log, "--- Log restarted: %s\n", *val_str);
fclose(cust->log);
cust->log = NULL;
}
if (**val_str)
{
char fname[255], *env;
strcpy(fname, cfgdir);
if (env = getenv("SICLOG"))
if (*env == '/')
strcpy(fname, getenv("SICLOG"));
else
strcat(fname, env);
else
strcat(fname, "log");
strcat(fname, "/");
strcat(fname, *val_str);
cust->log = fopen(fname, "a");
}
if (cust->log == NULL)
*val_str = "Unable to open logfile";
else
{
*val_str = NULL;
fprintf(cust->log, "--- Log started\n");
}
break;
default:
abort(); /* never */
}
}
else if (action & O_SWITCH)
{
switch (action & O_ACTIONMASK)
{
case O_GET:
if (option(cust->zopt_on, atoi(*val_str)))
*val_str = "on";
else if (option(cust->zopt_off, atoi(*val_str)))
*val_str = "off";
else
*val_str = "unset";
break;
case O_SET:
if (*val_ui == 0)
{
unset_option(cust->zopt_on, atoi(*val_str));
set_option(cust->zopt_off, atoi(*val_str));
}
else if (*val_ui == 1)
{
set_option(cust->zopt_on, atoi(*val_str));
unset_option(cust->zopt_off, atoi(*val_str));
}
else
abort();
break;
case O_CLEAR:
unset_option(cust->zopt_on, atoi(*val_str));
unset_option(cust->zopt_off, atoi(*val_str));
break;
default:
abort(); /* never */
}
}
else if (action & O_REWRITE)
{
#if !defined(HAVE_REGEXP)
abort();
#else
struct rewrite_ *r1, *r2;
char *delim;
switch (action & O_ACTIONMASK)
{
case O_CLEAR:
r2 = cust->rules;
while (r1 = r2)
{
r2 = r2->nextr;
free(r1);
}
cust->rules = NULL;
break;
case O_SET:
if (!(delim = strstr(*val_str, ">|>"))
&& !(delim = strstr(*val_str, ">+>")))
{
*val_str = "Invalid rule syntax.";
return;
}
*delim = '\0';
delim += 3;
r2 = cust->rules;
while (r2 && (r2->fmt != *val_ui || strcasecmp(r2->match, *val_str)))
r2 = r2->nextr;
if (r1 = r2)
free(r1->new);
else
{
static char rx_errbuf[1024];
int rx_err;
r2 = cust->rules;
while (r2 && r2->nextr)
r2 = r2->nextr;
r1 = (struct rewrite_ *) malloc(sizeof(struct rewrite_));
if (rx_err =regcomp(&(r1->preg),*val_str,REG_EXTENDED|REG_ICASE))
{
if (regerror(rx_err, &(r1->preg), rx_errbuf, 1024) > 1024)
*val_str="Error compiling regexp (rx_errbuf too small!)";
else
*val_str = rx_errbuf;
free(r1);
return;
}
if (cust->rules)
r2->nextr = r1;
else
cust->rules = r1;
r1->nextr = NULL;
r1->fmt = *val_ui;
r1->match = strdup(*val_str);
}
r1->type = (*(delim-2) == '+') ? 1 : 0;
r1->new = strdup(delim);
*val_str = NULL;
break;
case O_GET:
r2 = cust->rules;
if (*val_str && *val_ui <= F_MAX)
while (r2 && (r2->fmt != *val_ui ||
(*val_str && strcasecmp(r2->match, *val_str))))
r2 = r2->nextr;
else if (*val_ui <= F_MAX)
while (r2 && (r2->fmt != *val_ui || fnum < 0))
{
if (r2->fmt == *val_ui)
if (!++fnum)
break;
r2 = r2->nextr;
}
else while (r2 && ++fnum)
r2 = r2->nextr;
if (r2)
{
static char result[512];
sprintf(result, "%s>%c>%s", r2->match, (r2->type) ? '+' : '|',
r2->new);
*val_str = result;
*val_ui = (unsigned int) r2->fmt;
}
else
{
*val_str = NULL;
*val_ui = 0;
}
break;
}
#endif
}
else
{
unsigned int *val_i;
switch (action & O_TYPEMASK)
{
case O_MASK|O_ON:
val_i = &(cust->mask_on[atoi(*val_str)]);
break;
case O_MASK|O_OFF:
val_i = &(cust->mask_off[atoi(*val_str)]);
break;
case O_ON:
abort(); /* not implemented */
break;
case O_OFF:
abort(); /* not implemented */
break;
default:
abort();
}
switch (action & O_ACTIONMASK)
{
case O_CLEAR:
*val_i = 0;
break;
case O_GET:
*val_ui = *val_i;
break;
case O_SET:
*val_i = *val_ui;
break;
default:
abort(); /* never */
}
}
}
/* customize: get or set an option value
* recursion isn't implemented
* the caller is responsible for making sure that O_DEFAULT is only used
* with O_GET
*/
void
customize(action, channel, fnum, val_str, val_ui)
int fnum;
unsigned int action, *val_ui;
char **val_str, *channel;
{
struct channel_ *ctmp;
if (channel)
{
ctmp = get_channel(channel);
real_set(action, &(ctmp->custc), fnum, val_str, val_ui);
}
else switch (action & O_TARGETMASK)
{
case O_WINDOW:
real_set(action, &(current->custw), fnum, val_str, val_ui);
break;
case O_SERVER:
if (server)
real_set(action, &(server->custs), fnum, val_str, val_ui);
break;
case O_PROTO:
if (server && server->protocol)
real_set(action, &(protocust[server->protocol - 1]),
fnum, val_str, val_ui);
break;
case O_TOPLVL:
real_set(action, &(topcust), fnum, val_str, val_ui);
break;
case O_DEFAULT:
real_set(action, &(defcust), fnum, val_str, val_ui);
break;
default:
abort();
}
}
syntax highlighted by Code2HTML, v. 0.9.1