Skip to content

Instantly share code, notes, and snippets.

@pyropeter
Created March 20, 2011 19:31
Show Gist options
  • Select an option

  • Save pyropeter/878585 to your computer and use it in GitHub Desktop.

Select an option

Save pyropeter/878585 to your computer and use it in GitHub Desktop.
#0 0x00007fc9b86cf655 in raise () from /lib/libc.so.6
#1 0x00007fc9b86d0ad6 in abort () from /lib/libc.so.6
#2 0x00007fc9b96f5131 in g_assertion_message () from /usr/lib/libglib-2.0.so.0
#3 0x00007fc9b96f56d0 in g_assertion_message_expr ()
from /usr/lib/libglib-2.0.so.0
#4 0x000000000048f22c in signal_unref_full (rec=0x10ce060, remove=1)
at signals.c:64
#5 0x000000000048f4c5 in signal_emit_real (rec=0x10ce060,
params=<value optimized out>, va=<value optimized out>,
first_hook=<value optimized out>) at signals.c:287
#6 0x000000000048f91c in signal_emit (signal=<value optimized out>, params=4)
at signals.c:304
#7 0x000000000045b16d in irc_server_event (server=0x11b54f0,
line=0x11bb400 "PRIVMSG #pyropeter :foo", nick=0x11bb3d1 "PyroPeter",
address=0x11bb3db "~pyropeter@unixboard/users/pyropeter") at irc.c:304
#8 0x000000000048f3f3 in signal_emit_real (rec=0x10d2250,
params=<value optimized out>, va=<value optimized out>,
first_hook=<value optimized out>) at signals.c:258
#9 0x000000000048fa07 in signal_emit_id (signal_id=<value optimized out>,
params=4) at signals.c:322
#10 0x000000000048f3f3 in signal_emit_real (rec=0x10dd0d0,
params=<value optimized out>, va=<value optimized out>,
first_hook=<value optimized out>) at signals.c:258
#11 0x000000000048fa07 in signal_emit_id (signal_id=<value optimized out>,
params=2) at signals.c:322
#12 0x000000000045b037 in irc_parse_incoming (server=0x11b54f0) at irc.c:379
#13 0x000000000048212f in irssi_io_invoke (source=<value optimized out>,
condition=<value optimized out>, data=<value optimized out>) at misc.c:54
#14 0x00007fc9b96cfbf3 in g_main_context_dispatch ()
from /usr/lib/libglib-2.0.so.0
#15 0x00007fc9b96d03d0 in ?? () from /usr/lib/libglib-2.0.so.0
#16 0x00007fc9b96d066d in g_main_context_iteration ()
from /usr/lib/libglib-2.0.so.0
#17 0x000000000042c69c in main (argc=<value optimized out>,
argv=0x7fff7e3d7e68) at irssi.c:356
....
sig_print_text() 8/<g gPyroPeterg8/>g efoo
msglevel nohilight
nohilight
nickmatch
msglevel hilight
line
sig_print_text() >/< PyroPeter> foo
msglevel nohilight
nohilight
nickmatch
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 294, 3
signal_unref_full: gui print text finished, 3
II: 296, 6
signal_unref_full: print text, 6
return sig_print_text
II: 296, 5
signal_unref_full: print text, 5
II: 25, 5
signal_unref_full: message public, 5
II: 8658432, 0
**
ERROR:signals.c:64:signal_unref_full: assertion failed: (rec->refcount > 0)
sig_print_text() 8/<g gPyroPeterg8/>g efoo
msglevel nohilight
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 298, 2
signal_unref_full: gui print text, 2
II: 294, 3
signal_unref_full: gui print text finished, 3
II: 296, 5
signal_unref_full: print text, 5
II: 25, 5
signal_unref_full: message public, 5
II: 38581648, 0
**
ERROR:signals.c:64:signal_unref_full: assertion failed: (rec->refcount > 0)
/*
fe-messages.c : irssi
Copyright (C) 2000 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "module-formats.h"
#include "signals.h"
#include "commands.h"
#include "levels.h"
#include "misc.h"
#include "special-vars.h"
#include "settings.h"
#include "servers.h"
#include "channels.h"
#include "nicklist.h"
#include "ignore.h"
#include "window-items.h"
#include "fe-queries.h"
#include "hilight-text.h"
#include "printtext.h"
#define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c))
#define isnickchar(a) \
(i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
(a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
(a) == '|' || (a) == '\\' || (a) == '^')
GHashTable *printnicks;
/* convert _underlined_ and *bold* words (and phrases) to use real
underlining or bolding */
char *expand_emphasis(WI_ITEM_REC *item, const char *text)
{
GString *str;
char *ret;
int pos;
g_return_val_if_fail(text != NULL, NULL);
str = g_string_new(text);
for (pos = 0; pos < str->len; pos++) {
char type, *bgn, *end;
bgn = str->str + pos;
if (*bgn == '*')
type = 2; /* bold */
else if (*bgn == '_')
type = 31; /* underlined */
else
continue;
/* check that the beginning marker starts a word, and
that the matching end marker ends a word */
if ((pos > 0 && bgn[-1] != ' ') || !ishighalnum(bgn[1]))
continue;
if ((end = strchr(bgn+1, *bgn)) == NULL)
continue;
if (!ishighalnum(end[-1]) || ishighalnum(end[1]) ||
end[1] == type || end[1] == '*' || end[1] == '_')
continue;
if (IS_CHANNEL(item)) {
/* check that this isn't a _nick_, we don't want to
use emphasis on them. */
int found;
char c;
char *end2;
/* check if _foo_ is a nick */
c = end[1];
end[1] = '\0';
found = nicklist_find(CHANNEL(item), bgn) != NULL;
end[1] = c;
if (found) continue;
/* check if the whole 'word' (e.g. "_foo_^") is a nick
in "_foo_^ ", end will be the second _, end2 the ^ */
end2 = end;
while (isnickchar(end2[1]))
end2++;
c = end2[1];
end2[1] = '\0';
found = nicklist_find(CHANNEL(item), bgn) != NULL;
end2[1] = c;
if (found) continue;
}
/* allow only *word* emphasis, not *multiple words* */
if (!settings_get_bool("emphasis_multiword")) {
char *c;
for (c = bgn+1; c != end; c++) {
if (!ishighalnum(*c))
break;
}
if (c != end) continue;
}
if (settings_get_bool("emphasis_replace")) {
*bgn = *end = type;
pos += (end-bgn);
} else {
g_string_insert_c(str, pos, type);
pos += (end - bgn) + 2;
g_string_insert_c(str, pos++, type);
}
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static char *channel_get_nickmode_rec(NICK_REC *nickrec)
{
char *emptystr;
char *nickmode;
if (!settings_get_bool("show_nickmode"))
return g_strdup("");
emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
if (nickrec == NULL || nickrec->prefixes[0] == '\0')
nickmode = g_strdup(emptystr);
else {
nickmode = g_malloc(2);
nickmode[0] = nickrec->prefixes[0];
nickmode[1] = '\0';
}
return nickmode;
}
char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick)
{
g_return_val_if_fail(nick != NULL, NULL);
return channel_get_nickmode_rec(channel == NULL ? NULL :
nicklist_find(channel, nick));
}
static void sig_message_public(SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *target, NICK_REC *nickrec)
{
CHANNEL_REC *chanrec;
const char *printnick;
int for_me, print_channel, level;
char *nickmode, *color, *freemsg = NULL;
HILIGHT_REC *hilight;
level = MSGLEVEL_PUBLIC;
/* NOTE: this may return NULL if some channel is just closed with
/WINDOW CLOSE and server still sends the few last messages */
chanrec = channel_find(server, target);
if (nickrec == NULL && chanrec != NULL)
nickrec = nicklist_find(chanrec, nick);
print_channel = chanrec == NULL ||
!window_item_is_active((WI_ITEM_REC *) chanrec);
if (!print_channel && settings_get_bool("print_active_channel") &&
window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL)
print_channel = TRUE;
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg);
/* get nick mode & nick what to print the msg with
(in case there's multiple identical nicks) */
nickmode = channel_get_nickmode_rec(nickrec);
printnick = nickrec == NULL ? nick :
g_hash_table_lookup(printnicks, nickrec);
if (printnick == NULL)
printnick = nick;
int formatnum;
TEXT_DEST_REC dest;
format_create_dest(&dest, server, target, level, NULL);
hilight = hilight_match_nick_or_mask(server, target,
nick, address, MSGLEVEL_PUBLIC, msg);
if (hilight != NULL) {
// -nick or -mask
hilight_update_text_dest(&dest, hilight);
if (hilight->nick && !hilight->invert) {
// hilight needs to be handled specially
formatnum = print_channel ? TXT_PUBMSG_HILIGHT_CHANNEL :
TXT_PUBMSG_HILIGHT;
} else {
dest.level ^= MSGLEVEL_HILIGHT;
// hilight is handled by hilight-text.c, sig_print_text
formatnum = print_channel ? TXT_PUBMSG_CHANNEL :
TXT_PUBMSG;
}
} else {
// check if message starts with own nick
for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
nick_match_msg(chanrec, msg, server->nick);
if (for_me)
dest.level |= MSGLEVEL_HILIGHT;
if (for_me)
formatnum = print_channel ? TXT_PUBMSG_ME_CHANNEL :
TXT_PUBMSG_ME;
else
formatnum = print_channel ? TXT_PUBMSG_CHANNEL :
TXT_PUBMSG;
}
// check if format needs an additional 'color' parameter
if (formatnum == TXT_PUBMSG_HILIGHT_CHANNEL
|| formatnum == TXT_PUBMSG_HILIGHT) {
color = hilight_get_color(hilight);
printformat_dest(&dest, formatnum, color, printnick,
msg, nickmode);
} else {
printformat_dest(&dest, formatnum, printnick,
msg, nickmode);
}
g_free_not_null(nickmode);
g_free_not_null(freemsg);
g_free_not_null(color);
}
static void sig_message_private(SERVER_REC *server, const char *msg,
const char *nick, const char *address)
{
QUERY_REC *query;
char *freemsg = NULL;
query = query_find(server, nick);
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
printformat(server, nick, MSGLEVEL_MSGS,
query == NULL ? TXT_MSG_PRIVATE :
TXT_MSG_PRIVATE_QUERY, nick, address, msg);
g_free_not_null(freemsg);
}
static void sig_message_own_public(SERVER_REC *server, const char *msg,
const char *target)
{
WINDOW_REC *window;
CHANNEL_REC *channel;
char *nickmode;
char *freemsg = NULL;
int print_channel;
channel = channel_find(server, target);
if (channel != NULL)
target = channel->visible_name;
nickmode = channel_get_nickmode(channel, server->nick);
window = channel == NULL ? NULL :
window_item_window((WI_ITEM_REC *) channel);
print_channel = window == NULL ||
window->active != (WI_ITEM_REC *) channel;
if (!print_channel && settings_get_bool("print_active_channel") &&
window != NULL && g_slist_length(window->items) > 1)
print_channel = TRUE;
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) channel, msg);
if (!print_channel) {
printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
TXT_OWN_MSG, server->nick, msg, nickmode);
} else {
printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
TXT_OWN_MSG_CHANNEL, server->nick, target, msg, nickmode);
}
g_free_not_null(nickmode);
g_free_not_null(freemsg);
}
static void sig_message_own_private(SERVER_REC *server, const char *msg,
const char *target, const char *origtarget)
{
QUERY_REC *query;
char *freemsg = NULL;
g_return_if_fail(server != NULL);
g_return_if_fail(msg != NULL);
if (target == NULL) {
/* this should only happen if some special target failed and
we should display some error message. currently the special
targets are only ',' and '.'. */
g_return_if_fail(strcmp(origtarget, ",") == 0 ||
strcmp(origtarget, ".") == 0);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
*origtarget == ',' ? TXT_NO_MSGS_GOT :
TXT_NO_MSGS_SENT);
signal_stop();
return;
}
query = privmsg_get_query(server, target, TRUE, MSGLEVEL_MSGS);
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
printformat(server, target,
MSGLEVEL_MSGS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
query == NULL ? TXT_OWN_MSG_PRIVATE :
TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick);
g_free_not_null(freemsg);
}
static void sig_message_join(SERVER_REC *server, const char *channel,
const char *nick, const char *address)
{
printformat(server, channel, MSGLEVEL_JOINS,
TXT_JOIN, nick, address, channel);
}
static void sig_message_part(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
const char *reason)
{
printformat(server, channel, MSGLEVEL_PARTS,
TXT_PART, nick, address, channel, reason);
}
static void sig_message_quit(SERVER_REC *server, const char *nick,
const char *address, const char *reason)
{
WINDOW_REC *window;
GString *chans;
GSList *tmp, *windows;
char *print_channel;
int once, count;
if (ignore_check(server, nick, address, NULL, reason, MSGLEVEL_QUITS))
return;
print_channel = NULL;
once = settings_get_bool("show_quit_once");
count = 0; windows = NULL;
chans = g_string_new(NULL);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
if (!nicklist_find(rec, nick))
continue;
if (ignore_check(server, nick, address, rec->visible_name,
reason, MSGLEVEL_QUITS)) {
count++;
continue;
}
if (print_channel == NULL ||
active_win->active == (WI_ITEM_REC *) rec)
print_channel = rec->visible_name;
if (once)
g_string_append_printf(chans, "%s,", rec->visible_name);
else {
window = window_item_window((WI_ITEM_REC *) rec);
if (g_slist_find(windows, window) == NULL) {
windows = g_slist_append(windows, window);
printformat(server, rec->visible_name,
MSGLEVEL_QUITS,
TXT_QUIT, nick, address, reason,
rec->visible_name);
}
}
count++;
}
g_slist_free(windows);
if (!once) {
/* check if you had query with the nick and
display the quit there too */
QUERY_REC *query = query_find(server, nick);
if (query != NULL) {
printformat(server, nick, MSGLEVEL_QUITS,
TXT_QUIT, nick, address, reason, "");
}
}
if (once || count == 0) {
if (chans->len > 0)
g_string_truncate(chans, chans->len-1);
printformat(server, print_channel, MSGLEVEL_QUITS,
count <= 1 ? TXT_QUIT : TXT_QUIT_ONCE,
nick, address, reason, chans->str);
}
g_string_free(chans, TRUE);
}
static void sig_message_kick(SERVER_REC *server, const char *channel,
const char *nick, const char *kicker,
const char *address, const char *reason)
{
printformat(server, channel, MSGLEVEL_KICKS,
TXT_KICK, nick, channel, kicker, reason, address);
}
static void print_nick_change_channel(SERVER_REC *server, const char *channel,
const char *newnick, const char *oldnick,
const char *address,
int ownnick)
{
int level;
if (ignore_check(server, oldnick, address,
channel, newnick, MSGLEVEL_NICKS))
return;
level = MSGLEVEL_NICKS;
if (ownnick) level |= MSGLEVEL_NO_ACT;
printformat(server, channel, level,
ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED,
oldnick, newnick, channel, address);
}
static void print_nick_change(SERVER_REC *server, const char *newnick,
const char *oldnick, const char *address,
int ownnick)
{
GSList *tmp, *windows;
int msgprint;
msgprint = FALSE;
/* Print to each channel where the nick is.
Don't print more than once to the same window. */
windows = NULL;
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
WINDOW_REC *window =
window_item_window((WI_ITEM_REC *) channel);
if (nicklist_find(channel, newnick) == NULL ||
g_slist_find(windows, window) != NULL)
continue;
windows = g_slist_append(windows, window);
print_nick_change_channel(server, channel->visible_name,
newnick, oldnick, address, ownnick);
msgprint = TRUE;
}
g_slist_free(windows);
if (!msgprint && ownnick) {
printformat(server, NULL, MSGLEVEL_NICKS,
TXT_YOUR_NICK_CHANGED, oldnick, newnick, "",
address);
}
}
static void sig_message_nick(SERVER_REC *server, const char *newnick,
const char *oldnick, const char *address)
{
print_nick_change(server, newnick, oldnick, address, FALSE);
}
static void sig_message_own_nick(SERVER_REC *server, const char *newnick,
const char *oldnick, const char *address)
{
if (!settings_get_bool("show_own_nickchange_once"))
print_nick_change(server, newnick, oldnick, address, TRUE);
else {
printformat(server, NULL, MSGLEVEL_NICKS,
TXT_YOUR_NICK_CHANGED, oldnick, newnick, "",
address);
}
}
static void sig_message_invite(SERVER_REC *server, const char *channel,
const char *nick, const char *address)
{
char *str;
str = show_lowascii(channel);
printformat(server, NULL, MSGLEVEL_INVITES,
TXT_INVITE, nick, str, address);
g_free(str);
}
static void sig_message_topic(SERVER_REC *server, const char *channel,
const char *topic,
const char *nick, const char *address)
{
printformat(server, channel, MSGLEVEL_TOPICS,
*topic != '\0' ? TXT_NEW_TOPIC : TXT_TOPIC_UNSET,
nick, channel, topic, address);
}
static int printnick_exists(NICK_REC *first, NICK_REC *ignore,
const char *nick)
{
char *printnick;
while (first != NULL) {
if (first != ignore) {
printnick = g_hash_table_lookup(printnicks, first);
if (printnick != NULL && strcmp(printnick, nick) == 0)
return TRUE;
}
first = first->next;
}
return FALSE;
}
static NICK_REC *printnick_find_original(NICK_REC *nick)
{
while (nick != NULL) {
if (g_hash_table_lookup(printnicks, nick) == NULL)
return nick;
nick = nick->next;
}
return NULL;
}
static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
{
NICK_REC *firstnick;
GString *newnick;
char *nickhost, *p;
int n;
if (nick->host == NULL)
return;
firstnick = g_hash_table_lookup(channel->nicks, nick->nick);
if (firstnick->next == NULL)
return;
if (nick == channel->ownnick) {
/* own nick is being added, might be a nick change and
someone else having the original nick already in use.. */
nick = printnick_find_original(firstnick->next);
if (nick == NULL)
return; /* nope, we have it */
}
/* identical nick already exists, have to change it somehow.. */
p = strchr(nick->host, '@');
if (p == NULL) p = nick->host; else p++;
nickhost = g_strdup_printf("%s@%s", nick->nick, p);
p = strchr(nickhost+strlen(nick->nick), '.');
if (p != NULL) *p = '\0';
if (!printnick_exists(firstnick, nick, nickhost)) {
/* use nick@host */
g_hash_table_insert(printnicks, nick, nickhost);
return;
}
newnick = g_string_new(NULL);
n = 2;
do {
g_string_printf(newnick, "%s%d", nickhost, n);
n++;
} while (printnick_exists(firstnick, nick, newnick->str));
g_hash_table_insert(printnicks, nick, newnick->str);
g_string_free(newnick, FALSE);
g_free(nickhost);
}
static void sig_nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
{
char *nickname;
nickname = g_hash_table_lookup(printnicks, nick);
if (nickname != NULL) {
g_free(nickname);
g_hash_table_remove(printnicks, nick);
}
}
static void sig_nicklist_changed(CHANNEL_REC *channel, NICK_REC *nick)
{
sig_nicklist_remove(channel, nick);
sig_nicklist_new(channel, nick);
}
static void sig_channel_joined(CHANNEL_REC *channel)
{
NICK_REC *nick;
char *nickname;
/* channel->ownnick is set at this point - check if our own nick
has been changed, if it was set it back to the original nick and
change the previous original to something else */
nickname = g_hash_table_lookup(printnicks, channel->ownnick);
if (nickname == NULL)
return;
g_free(nickname);
g_hash_table_remove(printnicks, channel->ownnick);
/* our own nick is guaranteed to be the first in list */
nick = channel->ownnick->next;
while (nick != NULL) {
if (g_hash_table_lookup(printnicks, nick) == NULL) {
sig_nicklist_new(channel, nick);
break;
}
nick = nick->next;
}
}
static void g_hash_free_value(void *key, void *value)
{
g_free(value);
}
void fe_messages_init(void)
{
printnicks = g_hash_table_new((GHashFunc) g_direct_hash,
(GCompareFunc) g_direct_equal);
settings_add_bool("lookandfeel", "hilight_nick_matches", TRUE);
settings_add_bool("lookandfeel", "emphasis", TRUE);
settings_add_bool("lookandfeel", "emphasis_replace", FALSE);
settings_add_bool("lookandfeel", "emphasis_multiword", FALSE);
settings_add_bool("lookandfeel", "show_nickmode", TRUE);
settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE);
settings_add_bool("lookandfeel", "print_active_channel", FALSE);
settings_add_bool("lookandfeel", "show_quit_once", FALSE);
settings_add_bool("lookandfeel", "show_own_nickchange_once", FALSE);
signal_add_last("message public", (SIGNAL_FUNC) sig_message_public);
signal_add_last("message private", (SIGNAL_FUNC) sig_message_private);
signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public);
signal_add_last("message own_private", (SIGNAL_FUNC) sig_message_own_private);
signal_add_last("message join", (SIGNAL_FUNC) sig_message_join);
signal_add_last("message part", (SIGNAL_FUNC) sig_message_part);
signal_add_last("message quit", (SIGNAL_FUNC) sig_message_quit);
signal_add_last("message kick", (SIGNAL_FUNC) sig_message_kick);
signal_add_last("message nick", (SIGNAL_FUNC) sig_message_nick);
signal_add_last("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
signal_add_last("message invite", (SIGNAL_FUNC) sig_message_invite);
signal_add_last("message topic", (SIGNAL_FUNC) sig_message_topic);
signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
signal_add("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed);
signal_add("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new);
signal_add("channel joined", (SIGNAL_FUNC) sig_channel_joined);
}
void fe_messages_deinit(void)
{
g_hash_table_foreach(printnicks, (GHFunc) g_hash_free_value, NULL);
g_hash_table_destroy(printnicks);
signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
signal_remove("message part", (SIGNAL_FUNC) sig_message_part);
signal_remove("message quit", (SIGNAL_FUNC) sig_message_quit);
signal_remove("message kick", (SIGNAL_FUNC) sig_message_kick);
signal_remove("message nick", (SIGNAL_FUNC) sig_message_nick);
signal_remove("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
signal_remove("message invite", (SIGNAL_FUNC) sig_message_invite);
signal_remove("message topic", (SIGNAL_FUNC) sig_message_topic);
signal_remove("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed);
signal_remove("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new);
signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined);
}
/*
hilight-text.c : irssi
Copyright (C) 1999-2000 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "module-formats.h"
#include "signals.h"
#include "commands.h"
#include "levels.h"
#include "misc.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#include "servers.h"
#include "channels.h"
#include "nicklist.h"
#include "hilight-text.h"
#include "nickmatch-cache.h"
#include "printtext.h"
#include "formats.h"
// peter likes printf
#include <stdio.h>
#define PYRO(...) \
({ fprintf(stderr, __VA_ARGS__); \
fflush(stderr); })
static NICKMATCH_REC *nickmatch;
static int never_hilight_level, default_hilight_level;
GSList *hilights;
static void reset_level_cache(void)
{
GSList *tmp;
never_hilight_level = MSGLEVEL_ALL & ~default_hilight_level;
for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
HILIGHT_REC *rec = tmp->data;
if (never_hilight_level & rec->level)
never_hilight_level &= ~rec->level;
}
}
static void reset_cache(void)
{
reset_level_cache();
nickmatch_rebuild(nickmatch);
}
static void hilight_add_config(HILIGHT_REC *rec)
{
CONFIG_NODE *node;
g_return_if_fail(rec != NULL);
node = iconfig_node_traverse("(hilights", TRUE);
node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
iconfig_node_set_str(node, "text", rec->text);
if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
if (rec->color) iconfig_node_set_str(node, "color", rec->color);
if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
iconfig_node_set_bool(node, "nick", rec->nick);
iconfig_node_set_bool(node, "word", rec->word);
if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
if (rec->invert) iconfig_node_set_bool(node, "invert", TRUE);
if (rec->channels != NULL && *rec->channels != NULL) {
node = config_node_section(node, "channels", NODE_TYPE_LIST);
iconfig_node_add_list(node, rec->channels);
}
}
static void hilight_remove_config(HILIGHT_REC *rec)
{
CONFIG_NODE *node;
g_return_if_fail(rec != NULL);
node = iconfig_node_traverse("hilights", FALSE);
if (node != NULL) iconfig_node_list_remove(node, g_slist_index(hilights, rec));
}
static void hilight_destroy(HILIGHT_REC *rec)
{
g_return_if_fail(rec != NULL);
#ifdef HAVE_REGEX_H
if (rec->regexp_compiled) regfree(&rec->preg);
#endif
if (rec->channels != NULL) g_strfreev(rec->channels);
g_free_not_null(rec->color);
g_free_not_null(rec->act_color);
g_free(rec->text);
g_free(rec);
}
static void hilights_destroy_all(void)
{
g_slist_foreach(hilights, (GFunc) hilight_destroy, NULL);
g_slist_free(hilights);
hilights = NULL;
}
static void hilight_init_rec(HILIGHT_REC *rec)
{
#ifdef HAVE_REGEX_H
if (rec->regexp_compiled) regfree(&rec->preg);
rec->regexp_compiled = !rec->regexp ? FALSE :
regcomp(&rec->preg, rec->text, REG_EXTENDED|REG_ICASE) == 0;
#endif
}
void hilight_create(HILIGHT_REC *rec)
{
if (g_slist_find(hilights, rec) != NULL) {
hilight_remove_config(rec);
hilights = g_slist_remove(hilights, rec);
}
hilights = g_slist_append(hilights, rec);
hilight_add_config(rec);
hilight_init_rec(rec);
signal_emit("hilight created", 1, rec);
}
void hilight_remove(HILIGHT_REC *rec)
{
g_return_if_fail(rec != NULL);
hilight_remove_config(rec);
hilights = g_slist_remove(hilights, rec);
signal_emit("hilight destroyed", 1, rec);
hilight_destroy(rec);
}
static HILIGHT_REC *hilight_find(const char *text, char **channels)
{
GSList *tmp;
char **chan;
g_return_val_if_fail(text != NULL, NULL);
for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
HILIGHT_REC *rec = tmp->data;
if (g_strcasecmp(rec->text, text) != 0)
continue;
if ((channels == NULL && rec->channels == NULL))
return rec; /* no channels - ok */
if (channels != NULL && strcmp(*channels, "*") == 0)
return rec; /* ignore channels */
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
if (strarray_length(channels) != strarray_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
for (chan = channels; *chan != NULL; chan++) {
if (strarray_find(rec->channels, *chan) == -1)
break;
}
if (*chan == NULL)
return rec; /* channels ok */
}
return NULL;
}
static int hilight_match_text(HILIGHT_REC *rec, const char *text,
int *match_beg, int *match_end)
{
char *match;
if (rec->regexp) {
#ifdef HAVE_REGEX_H
regmatch_t rmatch[1];
if (rec->regexp_compiled &&
regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
if (rmatch[0].rm_so > 0 &&
match_beg != NULL && match_end != NULL) {
*match_beg = rmatch[0].rm_so;
*match_end = rmatch[0].rm_eo;
}
return TRUE;
}
#endif
} else {
match = rec->fullword ?
stristr_full(text, rec->text) :
stristr(text, rec->text);
if (match != NULL) {
if (match_beg != NULL && match_end != NULL) {
*match_beg = (int) (match-text);
*match_end = *match_beg + strlen(rec->text);
}
return TRUE;
}
}
return FALSE;
}
#define hilight_match_level(rec, level) \
(level & (((rec)->level != 0 ? rec->level : default_hilight_level)))
#define hilight_match_channel(rec, channel) \
((rec)->channels == NULL || ((channel) != NULL && \
strarray_find((rec)->channels, (channel)) != -1))
HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
int level, const char *str,
int *match_beg, int *match_end)
{
GSList *tmp;
CHANNEL_REC *chanrec;
NICK_REC *nickrec;
g_return_val_if_fail(str != NULL, NULL);
if ((never_hilight_level & level) == level)
return NULL;
if (nick != NULL) {
/* check nick mask hilights */
chanrec = channel_find(server, channel);
nickrec = chanrec == NULL ? NULL :
nicklist_find(chanrec, nick);
if (nickrec != NULL) {
HILIGHT_REC *rec;
if (nickrec->host == NULL)
nicklist_set_host(chanrec, nickrec, address);
rec = nickmatch_find(nickmatch, nickrec);
if (rec != NULL && hilight_match_level(rec, level))
return rec;
}
}
for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
HILIGHT_REC *rec = tmp->data;
if (!rec->nickmask && hilight_match_level(rec, level) &&
hilight_match_channel(rec, channel) &&
hilight_match_text(rec, str, match_beg, match_end))
return rec;
}
return NULL;
}
static char *hilight_get_act_color(HILIGHT_REC *rec)
{
g_return_val_if_fail(rec != NULL, NULL);
return g_strdup(rec->act_color != NULL ? rec->act_color :
rec->color != NULL ? rec->color :
settings_get_str("hilight_act_color"));
}
char *hilight_get_color(HILIGHT_REC *rec)
{
const char *color;
g_return_val_if_fail(rec != NULL, NULL);
color = rec->color != NULL ? rec->color :
settings_get_str("hilight_color");
return format_string_expand(color, NULL);
}
void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
{
dest->level |= MSGLEVEL_HILIGHT;
dest->hilight = rec;
if (rec->priority > 0)
dest->hilight_priority = rec->priority;
g_free_and_null(dest->hilight_color);
if (rec->act_color != NULL && strcmp(rec->act_color, "%n") == 0)
dest->level |= MSGLEVEL_NO_ACT;
else
dest->hilight_color = hilight_get_act_color(rec);
}
static void hilight_print(int index, HILIGHT_REC *rec);
static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
const char *stripped)
{
HILIGHT_REC *hilight;
char *color, *newstr;
int old_level, hilight_start, hilight_end, hilight_len;
int nick_match;
PYRO("sig_print_text() %s\n", text);
if (dest->level & MSGLEVEL_NOHILIGHT)
return;
PYRO("msglevel nohilight\n");
hilight_start = hilight_end = 0;
hilight = (dest->hilight != NULL) ? dest->hilight : hilight_match(
dest->server, dest->target,
NULL, NULL, dest->level, stripped,
&hilight_start,
&hilight_end);
if (hilight == NULL)
return;
PYRO("nohilight\n");
nick_match = hilight->nick && (dest->level & (MSGLEVEL_PUBLIC|MSGLEVEL_ACTIONS)) == MSGLEVEL_PUBLIC;
old_level = dest->level;
if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) {
/* update the level / hilight info */
hilight_update_text_dest(dest, hilight);
}
if (nick_match)
return; /* fe-messages.c should have taken care of this */
PYRO("nickmatch\n");
if (old_level & MSGLEVEL_HILIGHT) {
/* nick is highlighted, just set priority */
return;
}
PYRO("msglevel hilight\n");
color = hilight_get_color(hilight);
hilight_len = hilight_end-hilight_start;
if (!hilight->word) {
/* hilight whole line */
char *tmp = strip_codes(text);
newstr = g_strconcat(color, tmp, NULL);
g_free(tmp);
PYRO("line\n");
} else {
/* hilight part of the line */
GString *tmp;
char *middle;
int pos, color_pos, color_len;
tmp = g_string_new(NULL);
/* start of the line */
pos = strip_real_length(text, hilight_start, NULL, NULL);
g_string_append(tmp, text);
g_string_truncate(tmp, pos);
/* color */
g_string_append(tmp, color);
/* middle of the line, stripped */
middle = strip_codes(text+pos);
pos = tmp->len;
g_string_append(tmp, middle);
g_string_truncate(tmp, pos+hilight_len);
g_free(middle);
/* end of the line */
pos = strip_real_length(text, hilight_end,
&color_pos, &color_len);
if (color_pos > 0)
g_string_append_len(tmp, text+color_pos, color_len);
else {
/* no colors in line, change back to default */
g_string_append_c(tmp, 4);
g_string_append_c(tmp, FORMAT_STYLE_DEFAULTS);
}
g_string_append(tmp, text+pos);
newstr = tmp->str;
g_string_free(tmp, FALSE);
PYRO("else\n");
}
signal_emit("print text", 3, dest, newstr, stripped);
g_free(color);
g_free(newstr);
signal_stop();
PYRO("return sig_print_text\n");
}
HILIGHT_REC *hilight_match_nick_or_mask(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
int level, const char *msg)
{
HILIGHT_REC *rec;
rec = hilight_match(server, channel, nick, address,
level, msg, NULL, NULL);
return (rec != NULL && (rec->nick || rec->nickmask)) ? rec : NULL;
}
static void read_hilight_config(void)
{
CONFIG_NODE *node;
HILIGHT_REC *rec;
GSList *tmp;
char *text, *color;
hilights_destroy_all();
node = iconfig_node_traverse("hilights", FALSE);
if (node == NULL) {
reset_cache();
return;
}
tmp = config_node_first(node->value);
for (; tmp != NULL; tmp = config_node_next(tmp)) {
node = tmp->data;
if (node->type != NODE_TYPE_BLOCK)
continue;
text = config_node_get_str(node, "text", NULL);
if (text == NULL || *text == '\0')
continue;
rec = g_new0(HILIGHT_REC, 1);
hilights = g_slist_append(hilights, rec);
rec->text = g_strdup(text);
color = config_node_get_str(node, "color", NULL);
rec->color = color == NULL || *color == '\0' ? NULL :
g_strdup(color);
color = config_node_get_str(node, "act_color", NULL);
rec->act_color = color == NULL || *color == '\0' ? NULL :
g_strdup(color);
rec->level = config_node_get_int(node, "level", 0);
rec->priority = config_node_get_int(node, "priority", 0);
rec->nick = config_node_get_bool(node, "nick", TRUE);
rec->word = config_node_get_bool(node, "word", TRUE);
rec->nickmask = config_node_get_bool(node, "mask", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
rec->regexp = config_node_get_bool(node, "regexp", FALSE);
rec->invert = config_node_get_bool(node, "invert", FALSE);
hilight_init_rec(rec);
node = config_node_section(node, "channels", -1);
if (node != NULL) rec->channels = config_node_get_list(node);
}
reset_cache();
}
static void hilight_print(int index, HILIGHT_REC *rec)
{
char *chans, *levelstr;
GString *options;
options = g_string_new(NULL);
if (!rec->nick || !rec->word) {
if (rec->nick) g_string_append(options, "-nick ");
if (rec->word) g_string_append(options, "-word ");
}
if (rec->nickmask) g_string_append(options, "-mask ");
if (rec->fullword) g_string_append(options, "-full ");
if (rec->regexp) {
g_string_append(options, "-regexp ");
#ifdef HAVE_REGEX_H
if (!rec->regexp_compiled)
g_string_append(options, "[INVALID!] ");
#endif
}
if (rec->invert) g_string_append(options, "-invert ");
if (rec->priority != 0)
g_string_append_printf(options, "-priority %d ", rec->priority);
if (rec->color != NULL)
g_string_append_printf(options, "-color %s ", rec->color);
if (rec->act_color != NULL)
g_string_append_printf(options, "-actcolor %s ", rec->act_color);
chans = rec->channels == NULL ? NULL :
g_strjoinv(",", rec->channels);
levelstr = rec->level == 0 ? NULL :
bits2level(rec->level);
if (levelstr != NULL)
levelstr = g_strconcat(levelstr, " ", NULL);
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
TXT_HILIGHT_LINE, index, rec->text,
chans != NULL ? chans : "",
levelstr != NULL ? levelstr : "",
options->str);
g_free_not_null(chans);
g_free_not_null(levelstr);
g_string_free(options, TRUE);
}
static void cmd_hilight_show(void)
{
GSList *tmp;
int index;
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_HEADER);
index = 1;
for (tmp = hilights; tmp != NULL; tmp = tmp->next, index++) {
HILIGHT_REC *rec = tmp->data;
hilight_print(index, rec);
}
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER);
}
/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp] [-invert]
[-color <color>] [-actcolor <color>] [-level <level>]
[-channels <channels>] <text> */
static void cmd_hilight(const char *data)
{
GHashTable *optlist;
HILIGHT_REC *rec;
char *colorarg, *actcolorarg, *levelarg, *priorityarg, *chanarg, *text;
char **channels;
void *free_arg;
g_return_if_fail(data != NULL);
if (*data == '\0') {
cmd_hilight_show();
return;
}
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
PARAM_FLAG_GETREST, "hilight", &optlist, &text))
return;
chanarg = g_hash_table_lookup(optlist, "channels");
levelarg = g_hash_table_lookup(optlist, "level");
priorityarg = g_hash_table_lookup(optlist, "priority");
colorarg = g_hash_table_lookup(optlist, "color");
actcolorarg = g_hash_table_lookup(optlist, "actcolor");
if (*text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
channels = (chanarg == NULL || *chanarg == '\0') ? NULL :
g_strsplit(chanarg, ",", -1);
rec = hilight_find(text, channels);
if (rec == NULL) {
rec = g_new0(HILIGHT_REC, 1);
/* default to nick/word hilighting */
rec->nick = TRUE;
rec->word = TRUE;
rec->text = g_strdup(text);
rec->channels = channels;
} else {
g_strfreev(channels);
}
rec->level = (levelarg == NULL || *levelarg == '\0') ? 0 :
level2bits(replace_chars(levelarg, ',', ' '), NULL);
rec->priority = priorityarg == NULL ? 0 : atoi(priorityarg);
if (g_hash_table_lookup(optlist, "line") != NULL) {
rec->word = FALSE;
rec->nick = FALSE;
}
if (g_hash_table_lookup(optlist, "word") != NULL) {
rec->word = TRUE;
rec->nick = FALSE;
}
if (g_hash_table_lookup(optlist, "nick") != NULL)
rec->nick = TRUE;
rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL;
rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
rec->invert = g_hash_table_lookup(optlist, "invert") != NULL;
if (colorarg != NULL) {
g_free_and_null(rec->color);
if (*colorarg != '\0')
rec->color = g_strdup(colorarg);
}
if (actcolorarg != NULL) {
g_free_and_null(rec->act_color);
if (*actcolorarg != '\0')
rec->act_color = g_strdup(actcolorarg);
}
hilight_create(rec);
hilight_print(g_slist_index(hilights, rec)+1, rec);
cmd_params_free(free_arg);
reset_cache();
}
/* SYNTAX: DEHILIGHT <id>|<mask> */
static void cmd_dehilight(const char *data)
{
HILIGHT_REC *rec;
GSList *tmp;
if (is_numeric(data, ' ')) {
/* with index number */
tmp = g_slist_nth(hilights, atoi(data)-1);
rec = tmp == NULL ? NULL : tmp->data;
} else {
/* with mask */
char *chans[2] = { "*", NULL };
rec = hilight_find(data, chans);
}
if (rec == NULL)
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_NOT_FOUND, data);
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_REMOVED, rec->text);
hilight_remove(rec);
reset_cache();
}
}
static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
NICK_REC *nick)
{
GSList *tmp;
HILIGHT_REC *match;
char *nickmask;
int len, best_match;
if (nick->host == NULL)
return; /* don't check until host is known */
nickmask = g_strconcat(nick->nick, "!", nick->host, NULL);
best_match = 0; match = NULL;
for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
HILIGHT_REC *rec = tmp->data;
if (rec->nickmask &&
hilight_match_channel(rec, channel->name) &&
match_wildcards(rec->text, nickmask)) {
len = strlen(rec->text);
if (best_match < len) {
best_match = len;
match = rec;
}
}
}
g_free_not_null(nickmask);
if (match != NULL)
g_hash_table_insert(list, nick, match);
}
static void read_settings(void)
{
default_hilight_level = settings_get_level("hilight_level");
reset_level_cache();
}
void hilight_text_init(void)
{
settings_add_str("lookandfeel", "hilight_color", "%Y");
settings_add_str("lookandfeel", "hilight_act_color", "%M");
settings_add_level("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
read_settings();
nickmatch = nickmatch_init(hilight_nick_cache);
read_hilight_config();
signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight);
command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight);
command_set_options("hilight", "-color -actcolor -level -priority -channels nick word line mask full regexp invert");
}
void hilight_text_deinit(void)
{
hilights_destroy_all();
nickmatch_deinit(nickmatch);
signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight);
command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight);
}
/*
signals.c : irssi
Copyright (C) 1999-2002 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// peter likes printf
#include <stdio.h>
#define PYRO(...) \
({ fprintf(stderr, __VA_ARGS__); \
fflush(stderr); })
//#define PYRO(...) ({})
#include "module.h"
#include "signals.h"
#include "modules.h"
typedef struct _SignalHook {
struct _SignalHook *next;
int priority;
const char *module;
SIGNAL_FUNC func;
void *user_data;
} SignalHook;
typedef struct {
int id; /* signal id */
int refcount;
int emitting; /* signal is being emitted */
int stop_emit; /* this signal was stopped */
int continue_emit; /* this signal emit was continued elsewhere */
int remove_count; /* hooks were removed from signal */
SignalHook *hooks;
} Signal;
void *signal_user_data;
static GHashTable *signals;
static Signal *current_emitted_signal;
static SignalHook *current_emitted_hook;
#define signal_ref(signal) ++(signal)->refcount
#define signal_unref(signal) (signal_unref_full(signal, TRUE))
static int signal_unref_full(Signal *rec, int remove)
{
g_assert(rec->refcount > 0);
PYRO("signal_unref_full: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
if (--rec->refcount != 0)
return TRUE;
/* remove whole signal from memory */
if (rec->hooks != NULL) {
g_error("signal_unref(%s) : BUG - hook list wasn't empty",
signal_get_id_str(rec->id));
}
if (remove)
g_hash_table_remove(signals, GINT_TO_POINTER(rec->id));
g_free(rec);
return FALSE;
}
static void signal_hash_ref(void *key, Signal *rec)
{
PYRO("signal_hash_ref: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
signal_ref(rec);
}
static int signal_hash_unref(void *key, Signal *rec)
{
return !signal_unref_full(rec, FALSE);
}
void signal_add_full(const char *module, int priority,
const char *signal, SIGNAL_FUNC func, void *user_data)
{
signal_add_full_id(module, priority, signal_get_uniq_id(signal),
func, user_data);
}
/* bind a signal */
void signal_add_full_id(const char *module, int priority,
int signal_id, SIGNAL_FUNC func, void *user_data)
{
Signal *signal;
SignalHook *hook, **tmp;
g_return_if_fail(signal_id >= 0);
g_return_if_fail(func != NULL);
signal = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (signal == NULL) {
/* new signal */
signal = g_new0(Signal, 1);
signal->id = signal_id;
g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), signal);
}
hook = g_new0(SignalHook, 1);
hook->priority = priority;
hook->module = module;
hook->func = func;
hook->user_data = user_data;
/* insert signal to proper position in list */
for (tmp = &signal->hooks; ; tmp = &(*tmp)->next) {
if (*tmp == NULL) {
/* last in list */
*tmp = hook;
break;
} else if (priority <= (*tmp)->priority) {
/* insert before others with same priority */
hook->next = *tmp;
*tmp = hook;
break;
}
}
PYRO("signal_add_full_id: %s, %i\n", signal_get_id_str(signal->id), signal->refcount);
signal_ref(signal);
}
static void signal_remove_hook(Signal *rec, SignalHook **hook_pos)
{
SignalHook *hook;
hook = *hook_pos;
*hook_pos = hook->next;
g_free(hook);
signal_unref(rec);
}
/* Remove function from signal's emit list */
static int signal_remove_func(Signal *rec, SIGNAL_FUNC func, void *user_data)
{
SignalHook **hook;
for (hook = &rec->hooks; *hook != NULL; hook = &(*hook)->next) {
if ((*hook)->func == func && (*hook)->user_data == user_data) {
if (rec->emitting) {
/* mark it removed after emitting is done */
(*hook)->func = NULL;
rec->remove_count++;
} else {
/* remove the function from emit list */
signal_remove_hook(rec, hook);
}
return TRUE;
}
}
return FALSE;
}
void signal_remove_id(int signal_id, SIGNAL_FUNC func, void *user_data)
{
Signal *rec;
g_return_if_fail(signal_id >= 0);
g_return_if_fail(func != NULL);
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL)
signal_remove_func(rec, func, user_data);
}
/* unbind signal */
void signal_remove_full(const char *signal, SIGNAL_FUNC func, void *user_data)
{
g_return_if_fail(signal != NULL);
signal_remove_id(signal_get_uniq_id(signal), func, user_data);
}
static void signal_hooks_clean(Signal *rec)
{
SignalHook **hook, **next;
int count;
count = rec->remove_count;
rec->remove_count = 0;
for (hook = &rec->hooks; *hook != NULL; hook = next) {
next = &(*hook)->next;
if ((*hook)->func == NULL) {
next = hook;
signal_remove_hook(rec, hook);
if (--count == 0)
break;
}
}
}
static int signal_emit_real(Signal *rec, int params, va_list va,
SignalHook *first_hook)
{
const void *arglist[SIGNAL_MAX_ARGUMENTS];
Signal *prev_emitted_signal;
SignalHook *hook, *prev_emitted_hook;
int i, stopped, stop_emit_count, continue_emit_count;
for (i = 0; i < SIGNAL_MAX_ARGUMENTS; i++)
arglist[i] = i >= params ? NULL : va_arg(va, const void *);
/* signal_stop_by_name("signal"); signal_emit("signal", ...);
fails if we compare rec->stop_emit against 0. */
stop_emit_count = rec->stop_emit;
continue_emit_count = rec->continue_emit;
// this stops the crash
//PYRO("signal_emit_real: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
signal_ref(rec);
//PYRO("signal_emit_real: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
stopped = FALSE;
rec->emitting++;
prev_emitted_signal = current_emitted_signal;
prev_emitted_hook = current_emitted_hook;
current_emitted_signal = rec;
for (hook = first_hook; hook != NULL; hook = hook->next) {
if (hook->func == NULL)
continue; /* removed */
current_emitted_hook = hook;
#if SIGNAL_MAX_ARGUMENTS != 6
# error SIGNAL_MAX_ARGUMENTS changed - update code
#endif
signal_user_data = hook->user_data;
hook->func(arglist[0], arglist[1], arglist[2], arglist[3],
arglist[4], arglist[5]);
if (rec->continue_emit != continue_emit_count)
rec->continue_emit--;
if (rec->stop_emit != stop_emit_count) {
stopped = TRUE;
rec->stop_emit--;
break;
}
}
current_emitted_signal = prev_emitted_signal;
current_emitted_hook = prev_emitted_hook;
rec->emitting--;
signal_user_data = NULL;
if (!rec->emitting) {
g_assert(rec->stop_emit == 0);
g_assert(rec->continue_emit == 0);
if (rec->remove_count > 0)
signal_hooks_clean(rec);
}
PYRO("II: %i, %i\n", rec->id, rec->refcount);
signal_unref(rec);
return stopped;
}
int signal_emit(const char *signal, int params, ...)
{
Signal *rec;
va_list va;
int signal_id;
g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
signal_id = signal_get_uniq_id(signal);
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL) {
va_start(va, params);
signal_emit_real(rec, params, va, rec->hooks);
va_end(va);
}
return rec != NULL;
}
int signal_emit_id(int signal_id, int params, ...)
{
Signal *rec;
va_list va;
g_return_val_if_fail(signal_id >= 0, FALSE);
g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL) {
va_start(va, params);
signal_emit_real(rec, params, va, rec->hooks);
va_end(va);
}
return rec != NULL;
}
void signal_continue(int params, ...)
{
Signal *rec;
va_list va;
rec = current_emitted_signal;
if (rec == NULL || rec->emitting <= rec->continue_emit)
g_warning("signal_continue() : no signals are being emitted currently");
else {
va_start(va, params);
/* stop the signal */
if (rec->emitting > rec->stop_emit)
rec->stop_emit++;
/* re-emit */
rec->continue_emit++;
signal_emit_real(rec, params, va, current_emitted_hook->next);
va_end(va);
}
}
/* stop the current ongoing signal emission */
void signal_stop(void)
{
Signal *rec;
rec = current_emitted_signal;
if (rec == NULL)
g_warning("signal_stop() : no signals are being emitted currently");
else if (rec->emitting > rec->stop_emit)
rec->stop_emit++;
}
/* stop ongoing signal emission by signal name */
void signal_stop_by_name(const char *signal)
{
Signal *rec;
int signal_id;
signal_id = signal_get_uniq_id(signal);
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec == NULL)
g_warning("signal_stop_by_name() : unknown signal \"%s\"", signal);
else if (rec->emitting > rec->stop_emit)
rec->stop_emit++;
}
/* return the name of the signal that is currently being emitted */
const char *signal_get_emitted(void)
{
return signal_get_id_str(signal_get_emitted_id());
}
/* return the ID of the signal that is currently being emitted */
int signal_get_emitted_id(void)
{
Signal *rec;
rec = current_emitted_signal;
g_return_val_if_fail(rec != NULL, -1);
return rec->id;
}
/* return TRUE if specified signal was stopped */
int signal_is_stopped(int signal_id)
{
Signal *rec;
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
g_return_val_if_fail(rec != NULL, FALSE);
return rec->emitting <= rec->stop_emit;
}
static void signal_remove_module(void *signal, Signal *rec,
const char *module)
{
SignalHook **hook, **next;
for (hook = &rec->hooks; *hook != NULL; hook = next) {
next = &(*hook)->next;
if (strcasecmp((*hook)->module, module) == 0) {
next = hook;
signal_remove_hook(rec, hook);
}
}
}
/* remove all signals that belong to `module' */
void signals_remove_module(const char *module)
{
g_return_if_fail(module != NULL);
g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL);
g_hash_table_foreach(signals, (GHFunc) signal_remove_module,
(void *) module);
g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL);
}
void signals_init(void)
{
signals = g_hash_table_new(NULL, NULL);
}
static void signal_free(void *key, Signal *rec)
{
/* refcount-1 because we just referenced it ourself */
g_warning("signal_free(%s) : signal still has %d references:",
signal_get_id_str(rec->id), rec->refcount-1);
while (rec->hooks != NULL) {
g_warning(" - module '%s' function %p",
rec->hooks->module, rec->hooks->func);
signal_remove_hook(rec, &rec->hooks);
}
}
void signals_deinit(void)
{
g_hash_table_foreach(signals, (GHFunc) signal_hash_ref, NULL);
g_hash_table_foreach(signals, (GHFunc) signal_free, NULL);
g_hash_table_foreach_remove(signals, (GHRFunc) signal_hash_unref, NULL);
g_hash_table_destroy(signals);
module_uniq_destroy("signals");
}
diff --git a/docs/help/in/hilight.in b/docs/help/in/hilight.in
index fce51eb..c3ebe78 100644
--- a/docs/help/in/hilight.in
+++ b/docs/help/in/hilight.in
@@ -3,6 +3,7 @@
-mask: Match only for nick, <text> is a nick mask
-regexp: <text> is a regular expression
+ -invert: Suppress any hilights on a matching message
-full: <text> must match to full words
-nick: Hilight only the nick, not the whole line (default)
-word: Hilight only the word (default with non-public messages)
@@ -26,6 +27,10 @@ Hilight all messages from "mynick":
Hilight lines that were written by nicks from *.fi with bold green
/HILIGHT -color %%G -mask *!*@*.fi
+Hilight lines containing "mynick", except those from "root":
+ /HILIGHT mynick
+ /HILIGHT -invert -mask root!*@*
+
For regular expressions, see `man 7 regex`.
See also: DEHILIGHT, SET HILIGHT
diff --git a/src/core/signals.c b/src/core/signals.c
index 1f425ba..d213444 100644
--- a/src/core/signals.c
+++ b/src/core/signals.c
@@ -18,6 +18,13 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+// peter likes printf
+#include <stdio.h>
+#define PYRO(...) \
+ ({ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); })
+//#define PYRO(...) ({})
+
#include "module.h"
#include "signals.h"
#include "modules.h"
@@ -56,6 +63,8 @@ static int signal_unref_full(Signal *rec, int remove)
{
g_assert(rec->refcount > 0);
+PYRO("signal_unref_full: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
+
if (--rec->refcount != 0)
return TRUE;
@@ -74,6 +83,8 @@ static int signal_unref_full(Signal *rec, int remove)
static void signal_hash_ref(void *key, Signal *rec)
{
+
+PYRO("signal_hash_ref: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
signal_ref(rec);
}
@@ -127,6 +138,8 @@ void signal_add_full_id(const char *module, int priority,
}
}
+PYRO("signal_add_full_id: %s, %i\n", signal_get_id_str(signal->id), signal->refcount);
+
signal_ref(signal);
}
@@ -221,7 +234,10 @@ static int signal_emit_real(Signal *rec, int params, va_list va,
stop_emit_count = rec->stop_emit;
continue_emit_count = rec->continue_emit;
+// this stops the crash
+//PYRO("signal_emit_real: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
signal_ref(rec);
+//PYRO("signal_emit_real: %s, %i\n", signal_get_id_str(rec->id), rec->refcount);
stopped = FALSE;
rec->emitting++;
@@ -266,6 +282,8 @@ static int signal_emit_real(Signal *rec, int params, va_list va,
signal_hooks_clean(rec);
}
+
+PYRO("II: %i, %i\n", rec->id, rec->refcount);
signal_unref(rec);
return stopped;
}
diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c
index 846272f..50d4212 100644
--- a/src/fe-common/core/fe-messages.c
+++ b/src/fe-common/core/fe-messages.c
@@ -167,6 +167,8 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
int for_me, print_channel, level;
char *nickmode, *color, *freemsg = NULL;
HILIGHT_REC *hilight;
+
+ level = MSGLEVEL_PUBLIC;
/* NOTE: this may return NULL if some channel is just closed with
/WINDOW CLOSE and server still sends the few last messages */
@@ -174,22 +176,12 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
if (nickrec == NULL && chanrec != NULL)
nickrec = nicklist_find(chanrec, nick);
- for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
- nick_match_msg(chanrec, msg, server->nick);
- hilight = for_me ? NULL :
- hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
- color = (hilight == NULL) ? NULL : hilight_get_color(hilight);
-
print_channel = chanrec == NULL ||
!window_item_is_active((WI_ITEM_REC *) chanrec);
if (!print_channel && settings_get_bool("print_active_channel") &&
window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL)
print_channel = TRUE;
- level = MSGLEVEL_PUBLIC;
- if (for_me)
- level |= MSGLEVEL_HILIGHT;
-
if (settings_get_bool("emphasis"))
msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg);
@@ -201,29 +193,52 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
if (printnick == NULL)
printnick = nick;
- if (color != NULL) {
- /* highlighted nick */
- TEXT_DEST_REC dest;
- format_create_dest(&dest, server, target, level, NULL);
- hilight_update_text_dest(&dest,hilight);
- if (!print_channel) /* message to active channel in window */
- printformat_dest(&dest, TXT_PUBMSG_HILIGHT, color,
- printnick, msg, nickmode);
- else /* message to not existing/active channel */
- printformat_dest(&dest, TXT_PUBMSG_HILIGHT_CHANNEL,
- color, printnick, target, msg,
- nickmode);
+
+
+ int formatnum;
+ TEXT_DEST_REC dest;
+ format_create_dest(&dest, server, target, level, NULL);
+ hilight = hilight_match_nick_or_mask(server, target,
+ nick, address, MSGLEVEL_PUBLIC, msg);
+
+ if (hilight != NULL) {
+ // -nick or -mask
+ hilight_update_text_dest(&dest, hilight);
+ if (hilight->nick && !hilight->invert) {
+ // hilight needs to be handled specially
+ formatnum = print_channel ? TXT_PUBMSG_HILIGHT_CHANNEL :
+ TXT_PUBMSG_HILIGHT;
+ } else {
+ dest.level ^= MSGLEVEL_HILIGHT;
+ // hilight is handled by hilight-text.c, sig_print_text
+ formatnum = print_channel ? TXT_PUBMSG_CHANNEL :
+ TXT_PUBMSG;
+ }
} else {
- if (!print_channel)
- printformat(server, target, level,
- for_me ? TXT_PUBMSG_ME : TXT_PUBMSG,
- printnick, msg, nickmode);
+ // check if message starts with own nick
+ for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
+ nick_match_msg(chanrec, msg, server->nick);
+ if (for_me)
+ dest.level |= MSGLEVEL_HILIGHT;
+
+ if (for_me)
+ formatnum = print_channel ? TXT_PUBMSG_ME_CHANNEL :
+ TXT_PUBMSG_ME;
else
- printformat(server, target, level,
- for_me ? TXT_PUBMSG_ME_CHANNEL :
- TXT_PUBMSG_CHANNEL,
- printnick, target, msg, nickmode);
- }
+ formatnum = print_channel ? TXT_PUBMSG_CHANNEL :
+ TXT_PUBMSG;
+ }
+
+ // check if format needs an additional 'color' parameter
+ if (formatnum == TXT_PUBMSG_HILIGHT_CHANNEL
+ || formatnum == TXT_PUBMSG_HILIGHT) {
+ color = hilight_get_color(hilight);
+ printformat_dest(&dest, formatnum, color, printnick,
+ msg, nickmode);
+ } else {
+ printformat_dest(&dest, formatnum, printnick,
+ msg, nickmode);
+ }
g_free_not_null(nickmode);
g_free_not_null(freemsg);
diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h
index 6c55a06..042c211 100644
--- a/src/fe-common/core/formats.h
+++ b/src/fe-common/core/formats.h
@@ -3,6 +3,7 @@
#include "themes.h"
#include "fe-windows.h"
+#include "hilight-rec.h"
#define GUI_PRINT_FLAG_BOLD 0x0001
#define GUI_PRINT_FLAG_REVERSE 0x0002
@@ -52,6 +53,7 @@ typedef struct _TEXT_DEST_REC {
int hilight_priority;
char *hilight_color;
int flags;
+ HILIGHT_REC *hilight;
} TEXT_DEST_REC;
#define window_get_theme(window) \
diff --git a/src/fe-common/core/hilight-text.c b/src/fe-common/core/hilight-text.c
index f501db9..67694e4 100644
--- a/src/fe-common/core/hilight-text.c
+++ b/src/fe-common/core/hilight-text.c
@@ -36,6 +36,12 @@
#include "printtext.h"
#include "formats.h"
+// peter likes printf
+#include <stdio.h>
+#define PYRO(...) \
+ ({ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); })
+
static NICKMATCH_REC *nickmatch;
static int never_hilight_level, default_hilight_level;
GSList *hilights;
@@ -78,6 +84,7 @@ static void hilight_add_config(HILIGHT_REC *rec)
if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
+ if (rec->invert) iconfig_node_set_bool(node, "invert", TRUE);
if (rec->channels != NULL && *rec->channels != NULL) {
node = config_node_section(node, "channels", NODE_TYPE_LIST);
@@ -299,6 +306,8 @@ void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
{
dest->level |= MSGLEVEL_HILIGHT;
+ dest->hilight = rec;
+
if (rec->priority > 0)
dest->hilight_priority = rec->priority;
@@ -319,17 +328,24 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
int old_level, hilight_start, hilight_end, hilight_len;
int nick_match;
+ PYRO("sig_print_text() %s\n", text);
+
if (dest->level & MSGLEVEL_NOHILIGHT)
return;
+ PYRO("msglevel nohilight\n");
+
hilight_start = hilight_end = 0;
- hilight = hilight_match(dest->server, dest->target,
+ hilight = (dest->hilight != NULL) ? dest->hilight : hilight_match(
+ dest->server, dest->target,
NULL, NULL, dest->level, stripped,
&hilight_start,
&hilight_end);
if (hilight == NULL)
return;
+ PYRO("nohilight\n");
+
nick_match = hilight->nick && (dest->level & (MSGLEVEL_PUBLIC|MSGLEVEL_ACTIONS)) == MSGLEVEL_PUBLIC;
old_level = dest->level;
@@ -341,11 +357,15 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
if (nick_match)
return; /* fe-messages.c should have taken care of this */
+ PYRO("nickmatch\n");
+
if (old_level & MSGLEVEL_HILIGHT) {
/* nick is highlighted, just set priority */
return;
}
+ PYRO("msglevel hilight\n");
+
color = hilight_get_color(hilight);
hilight_len = hilight_end-hilight_start;
@@ -354,6 +374,8 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
char *tmp = strip_codes(text);
newstr = g_strconcat(color, tmp, NULL);
g_free(tmp);
+
+ PYRO("line\n");
} else {
/* hilight part of the line */
GString *tmp;
@@ -391,6 +413,8 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
newstr = tmp->str;
g_string_free(tmp, FALSE);
+
+ PYRO("else\n");
}
signal_emit("print text", 3, dest, newstr, stripped);
@@ -399,9 +423,11 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
g_free(newstr);
signal_stop();
+
+ PYRO("return sig_print_text\n");
}
-HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
+HILIGHT_REC *hilight_match_nick_or_mask(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
int level, const char *msg)
{
@@ -409,7 +435,7 @@ HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
rec = hilight_match(server, channel, nick, address,
level, msg, NULL, NULL);
- return (rec == NULL || !rec->nick) ? NULL : rec;
+ return (rec != NULL && (rec->nick || rec->nickmask)) ? rec : NULL;
}
static void read_hilight_config(void)
@@ -459,6 +485,7 @@ static void read_hilight_config(void)
rec->nickmask = config_node_get_bool(node, "mask", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
rec->regexp = config_node_get_bool(node, "regexp", FALSE);
+ rec->invert = config_node_get_bool(node, "invert", FALSE);
hilight_init_rec(rec);
@@ -489,6 +516,7 @@ static void hilight_print(int index, HILIGHT_REC *rec)
g_string_append(options, "[INVALID!] ");
#endif
}
+ if (rec->invert) g_string_append(options, "-invert ");
if (rec->priority != 0)
g_string_append_printf(options, "-priority %d ", rec->priority);
@@ -528,7 +556,7 @@ static void cmd_hilight_show(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER);
}
-/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp]
+/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp] [-invert]
[-color <color>] [-actcolor <color>] [-level <level>]
[-channels <channels>] <text> */
static void cmd_hilight(const char *data)
@@ -595,6 +623,7 @@ static void cmd_hilight(const char *data)
rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL;
rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
+ rec->invert = g_hash_table_lookup(optlist, "invert") != NULL;
if (colorarg != NULL) {
g_free_and_null(rec->color);
@@ -696,7 +725,7 @@ void hilight_text_init(void)
command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight);
command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight);
- command_set_options("hilight", "-color -actcolor -level -priority -channels nick word line mask full regexp");
+ command_set_options("hilight", "-color -actcolor -level -priority -channels nick word line mask full regexp invert");
}
void hilight_text_deinit(void)
diff --git a/src/fe-common/core/hilight-text.h b/src/fe-common/core/hilight-text.h
index 74c5878..3741eef 100644
--- a/src/fe-common/core/hilight-text.h
+++ b/src/fe-common/core/hilight-text.h
@@ -1,35 +1,8 @@
#ifndef __HILIGHT_TEXT_H
#define __HILIGHT_TEXT_H
-#ifdef HAVE_REGEX_H
-# include <regex.h>
-#endif
-
#include "formats.h"
-
-typedef struct _HILIGHT_REC HILIGHT_REC;
-
-struct _HILIGHT_REC {
- char *text;
-
- char **channels; /* if non-NULL, check the text only from these channels */
- int level; /* match only messages with this level, 0=default */
- char *color; /* if starts with number, \003 is automatically
- inserted before it. */
- char *act_color; /* color for window activity */
- int priority;
-
- unsigned int nick:1; /* hilight only nick if possible */
- unsigned int word:1; /* hilight only word, not full line */
-
- unsigned int nickmask:1; /* `text' is a nick mask */
- unsigned int fullword:1; /* match `text' only for full words */
- unsigned int regexp:1; /* `text' is a regular expression */
-#ifdef HAVE_REGEX_H
- unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
- regex_t preg;
-#endif
-};
+#include "hilight-rec.h"
extern GSList *hilights;
@@ -38,7 +11,7 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
int level, const char *str,
int *match_beg, int *match_end);
-HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
+HILIGHT_REC *hilight_match_nick_or_mask(SERVER_REC *server, const char *channel,
const char *nick, const char *address,
int level, const char *msg);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment