summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2021-01-01 20:09:10 -0500
committerJune McEnroe <june@causal.agency>2021-01-01 20:09:10 -0500
commit4b883177dc025db24473e62469f97631a12ad536 (patch)
tree0039251bc3093c68c44eb95fb7a1051168fe3acf
parentFactor out reply count checking and decrementing (diff)
downloadcatgirl-4b883177dc025db24473e62469f97631a12ad536.tar.gz
catgirl-4b883177dc025db24473e62469f97631a12ad536.zip
Split ignore fields to avoid over-eager * matching
Split ignore fields and match each separately to avoid an early *
eagerly matching across several fields. For example, "* JOIN * *" should
not match messages which happen to contain the word "JOIN" followed by
two other words.

Ignore capacity is reduced to 64 to keep the size of the array the same.
I don't think it's an issue.
-rw-r--r--chat.h15
-rw-r--r--command.c30
-rw-r--r--ignore.c89
3 files changed, 77 insertions, 57 deletions
diff --git a/chat.h b/chat.h
index c7af680..b06d383 100644
--- a/chat.h
+++ b/chat.h
@@ -353,13 +353,16 @@ void urlOpenCount(uint id, uint count);
 void urlOpenMatch(uint id, const char *str);
 void urlCopyMatch(uint id, const char *str);
 
-enum { IgnoreCap = 256 };
+enum { IgnoreCap = 64 };
 extern struct Ignore {
-	size_t len;
-	char *patterns[IgnoreCap];
-} ignore;
-const char *ignoreAdd(const char *pattern);
-bool ignoreRemove(const char *pattern);
+	char *mask;
+	char *cmd;
+	char *chan;
+	char *mesg;
+} ignores[IgnoreCap];
+struct Ignore ignoreParse(char *pattern);
+struct Ignore ignoreAdd(const char *pattern);
+bool ignoreRemove(struct Ignore ignore);
 enum Heat ignoreCheck(enum Heat heat, uint id, const struct Message *msg);
 
 extern bool logEnable;
diff --git a/command.c b/command.c
index 43870f1..c71bebc 100644
--- a/command.c
+++ b/command.c
@@ -388,16 +388,19 @@ static void commandCopy(uint id, char *params) {
 
 static void commandIgnore(uint id, char *params) {
 	if (params) {
-		const char *pattern = ignoreAdd(params);
+		struct Ignore ignore = ignoreAdd(params);
 		uiFormat(
-			id, Cold, NULL, "Ignoring \3%02d%s\3",
-			Brown, pattern
+			id, Cold, NULL, "Ignoring \3%02d%s %s %s %s",
+			Brown, ignore.mask,
+			(ignore.cmd ?: ""), (ignore.chan ?: ""), (ignore.mesg ?: "")
 		);
 	} else {
-		for (size_t i = 0; i < ignore.len; ++i) {
+		for (size_t i = 0; i < IgnoreCap && ignores[i].mask; ++i) {
 			uiFormat(
-				Network, Warm, NULL, "Ignoring \3%02d%s\3",
-				Brown, ignore.patterns[i]
+				Network, Warm, NULL, "Ignoring \3%02d%s %s %s %s",
+				Brown, ignores[i].mask,
+				(ignores[i].cmd ?: ""), (ignores[i].chan ?: ""),
+				(ignores[i].mesg ?: "")
 			);
 		}
 	}
@@ -405,14 +408,13 @@ static void commandIgnore(uint id, char *params) {
 
 static void commandUnignore(uint id, char *params) {
 	if (!params) return;
-	if (ignoreRemove(params)) {
-		uiFormat(
-			id, Cold, NULL, "No longer ignoring \3%02d%s\3",
-			Brown, params
-		);
-	} else {
-		uiFormat(id, Cold, NULL, "Not ignoring \3%02d%s\3", Brown, params);
-	}
+	struct Ignore ignore = ignoreParse(params);
+	bool found = ignoreRemove(ignore);
+	uiFormat(
+		id, Cold, NULL, "%s ignoring \3%02d%s %s %s %s",
+		(found ? "No longer" : "Not"), Brown, ignore.mask,
+		(ignore.cmd ?: ""), (ignore.chan ?: ""), (ignore.mesg ?: "")
+	);
 }
 
 static void commandExec(uint id, char *params) {
diff --git a/ignore.c b/ignore.c
index f8e4d59..5a9d296 100644
--- a/ignore.c
+++ b/ignore.c
@@ -35,55 +35,70 @@
 
 #include "chat.h"
 
-struct Ignore ignore;
+struct Ignore ignores[IgnoreCap];
+static size_t len;
 
-const char *ignoreAdd(const char *pattern) {
-	if (ignore.len == IgnoreCap) errx(EX_CONFIG, "ignore limit exceeded");
-	uint ex = 0, sp = 0;
-	for (const char *ch = pattern; *ch; ++ch) {
-		if (*ch == '!') ex++;
-		if (*ch == ' ') sp++;
-	}
-	char **dest = &ignore.patterns[ignore.len++];
-	int n = 0;
-	if (!ex && !sp) {
-		n = asprintf(dest, "%s!*@* * * *", pattern);
-	} else if (sp < 1) {
-		n = asprintf(dest, "%s * * *", pattern);
-	} else if (sp < 2) {
-		n = asprintf(dest, "%s * *", pattern);
-	} else if (sp < 3) {
-		n = asprintf(dest, "%s *", pattern);
+struct Ignore ignoreParse(char *pattern) {
+	struct Ignore ignore = {0};
+	ignore.mask = strsep(&pattern, " ");
+	ignore.cmd  = strsep(&pattern, " ");
+	ignore.chan = strsep(&pattern, " ");
+	ignore.mesg = pattern;
+	return ignore;
+}
+
+struct Ignore ignoreAdd(const char *pattern) {
+	if (len == IgnoreCap) errx(EX_CONFIG, "ignore limit exceeded");
+	char *own;
+	if (!strchr(pattern, '!') && !strchr(pattern, ' ')) {
+		int n = asprintf(&own, "%s!*@*", pattern);
+		if (n < 0) err(EX_OSERR, "asprintf");
 	} else {
-		*dest = strdup(pattern);
+		own = strdup(pattern);
+		if (!own) err(EX_OSERR, "strdup");
 	}
-	if (n < 0) err(EX_OSERR, "asprintf");
-	if (!*dest) err(EX_OSERR, "strdup");
-	return *dest;
+	struct Ignore ignore = ignoreParse(own);
+	ignores[len++] = ignore;
+	return ignore;
 }
 
-bool ignoreRemove(const char *pattern) {
+bool ignoreRemove(struct Ignore ignore) {
 	bool found = false;
-	for (size_t i = 0; i < ignore.len; ++i) {
-		if (strcasecmp(ignore.patterns[i], pattern)) continue;
-		free(ignore.patterns[i]);
-		ignore.patterns[i] = ignore.patterns[--ignore.len];
+	for (size_t i = len - 1; i < len; --i) {
+		if (!ignores[i].cmd != !ignore.cmd) continue;
+		if (!ignores[i].chan != !ignore.chan) continue;
+		if (!ignores[i].mesg != !ignore.mesg) continue;
+		if (strcasecmp(ignores[i].mask, ignore.mask)) continue;
+		if (ignore.cmd && strcasecmp(ignores[i].cmd, ignore.cmd)) continue;
+		if (ignore.chan && strcasecmp(ignores[i].chan, ignore.chan)) continue;
+		if (ignore.mesg && strcasecmp(ignores[i].mesg, ignore.mesg)) continue;
+		free(ignores[i].mask);
+		ignores[i] = ignores[--len];
+		ignores[len] = (struct Ignore) {0};
 		found = true;
 	}
 	return found;
 }
 
+static bool ignoreTest(
+	struct Ignore ignore, const char *mask, uint id, const struct Message *msg
+) {
+	if (fnmatch(ignore.mask, mask, FNM_CASEFOLD)) return false;
+	if (!ignore.cmd) return true;
+	if (fnmatch(ignore.cmd, msg->cmd, FNM_CASEFOLD)) return false;
+	if (!ignore.chan) return true;
+	if (fnmatch(ignore.chan, idNames[id], FNM_CASEFOLD)) return false;
+	if (!ignore.mesg) return true;
+	if (!msg->params[1]) return false;
+	return !fnmatch(ignore.mesg, msg->params[1], FNM_CASEFOLD);
+}
+
 enum Heat ignoreCheck(enum Heat heat, uint id, const struct Message *msg) {
-	if (!ignore.len) return heat;
-	char match[512];
-	snprintf(
-		match, sizeof(match), "%s!%s@%s %s %s %s",
-		msg->nick, msg->user, msg->host,
-		msg->cmd, idNames[id], (msg->params[1] ?: "")
-	);
-	for (size_t i = 0; i < ignore.len; ++i) {
-		if (fnmatch(ignore.patterns[i], match, FNM_CASEFOLD)) continue;
-		return Ice;
+	if (!len) return heat;
+	char mask[512];
+	snprintf(mask, sizeof(mask), "%s!%s@%s", msg->nick, msg->user, msg->host);
+	for (size_t i = 0; i < len; ++i) {
+		if (ignoreTest(ignores[i], mask, id, msg)) return Ice;
 	}
 	return heat;
 }