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