summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile1
-rw-r--r--README3
-rw-r--r--catgirl.74
-rw-r--r--chat.h30
-rw-r--r--color.c50
-rw-r--r--format.c20
-rw-r--r--handle.c65
-rw-r--r--input.c4
-rw-r--r--tag.c20
-rw-r--r--ui.c8
10 files changed, 117 insertions, 88 deletions
diff --git a/Makefile b/Makefile
index 9130655..e03c1dc 100644
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,7 @@ MAN1 = catgirl.1
 -include config.mk
 
 OBJS += chat.o
+OBJS += color.o
 OBJS += edit.o
 OBJS += event.o
 OBJS += format.o
diff --git a/README b/README
index 5957c57..06da8df 100644
--- a/README
+++ b/README
@@ -39,6 +39,7 @@ FILES
      input.c    input command handling
      irc.c      TLS client connection
      format.c   IRC formatting
+     color.c    nick and channel coloring
      ui.c       cursed UI
      term.c     terminal features unsupported by curses
      edit.c     line editing
@@ -53,4 +54,4 @@ FILES
 SEE ALSO
      catgirl(1), sandman(1)
 
-Causal Agency                  January 25, 2019                  Causal Agency
+Causal Agency                  February 25, 2019                 Causal Agency
diff --git a/catgirl.7 b/catgirl.7
index 638b3f2..cae56bb 100644
--- a/catgirl.7
+++ b/catgirl.7
@@ -1,4 +1,4 @@
-.Dd January 25, 2019
+.Dd February 25, 2019
 .Dt CATGIRL 7
 .Os "Causal Agency"
 .
@@ -79,6 +79,8 @@ input command handling
 TLS client connection
 .It Pa format.c
 IRC formatting
+.It Pa color.c
+nick and channel coloring
 .It Pa ui.c
 cursed UI
 .It Pa term.c
diff --git a/chat.h b/chat.h
index d6c234c..6f08a01 100644
--- a/chat.h
+++ b/chat.h
@@ -52,6 +52,18 @@ void eventWait(const char *argv[static 2]);
 void eventPipe(const char *argv[static 2]);
 noreturn void eventLoop(void);
 
+struct Tag {
+	size_t id;
+	const char *name;
+};
+
+enum { TagsLen = 256 };
+const struct Tag TagNone;
+const struct Tag TagStatus;
+const struct Tag TagRaw;
+struct Tag tagFind(const char *name);
+struct Tag tagFor(const char *name);
+
 enum IRCColor {
 	IRCWhite,
 	IRCBlack,
@@ -80,19 +92,6 @@ enum {
 	IRCUnderline = 037,
 };
 
-struct Tag {
-	size_t id;
-	const char *name;
-	enum IRCColor color;
-};
-
-enum { TagsLen = 256 };
-const struct Tag TagNone;
-const struct Tag TagStatus;
-const struct Tag TagRaw;
-struct Tag tagFind(const char *name);
-struct Tag tagFor(const char *name, enum IRCColor color);
-
 struct Format {
 	const wchar_t *str;
 	size_t len;
@@ -102,7 +101,10 @@ struct Format {
 };
 void formatReset(struct Format *format);
 bool formatParse(struct Format *format, const wchar_t *split);
-enum IRCColor formatColor(const char *str);
+
+enum IRCColor colorGen(const char *str);
+struct Tag colorTag(struct Tag tag, const char *gen);
+enum IRCColor colorFor(struct Tag tag);
 
 void handle(char *line);
 void input(struct Tag tag, char *line);
diff --git a/color.c b/color.c
new file mode 100644
index 0000000..f713451
--- /dev/null
+++ b/color.c
@@ -0,0 +1,50 @@
+/* Copyright (C) 2019  C. McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+
+#include "chat.h"
+
+// Adapted from <https://github.com/cbreeden/fxhash/blob/master/lib.rs>.
+static uint32_t hashChar(uint32_t hash, char ch) {
+	hash = (hash << 5) | (hash >> 27);
+	hash ^= ch;
+	hash *= 0x27220A95;
+	return hash;
+}
+
+enum IRCColor colorGen(const char *str) {
+	if (!str) return IRCDefault;
+	uint32_t hash = 0;
+	for (; str[0]; ++str) {
+		hash = hashChar(hash, str[0]);
+	}
+	while (IRCBlack == (hash & IRCLightGray)) {
+		hash = hashChar(hash, '\0');
+	}
+	return (hash & IRCLightGray);
+}
+
+static enum IRCColor colors[TagsLen];
+
+struct Tag colorTag(struct Tag tag, const char *gen) {
+	if (!colors[tag.id]) colors[tag.id] = 1 + colorGen(gen);
+	return tag;
+}
+
+enum IRCColor colorFor(struct Tag tag) {
+	return colors[tag.id] ? colors[tag.id] - 1 : IRCDefault;
+}
diff --git a/format.c b/format.c
index 02406e3..e2a6bb1 100644
--- a/format.c
+++ b/format.c
@@ -21,26 +21,6 @@
 
 #include "chat.h"
 
-// Adapted from <https://github.com/cbreeden/fxhash/blob/master/lib.rs>.
-static uint32_t hashChar(uint32_t hash, char ch) {
-	hash = (hash << 5) | (hash >> 27);
-	hash ^= ch;
-	hash *= 0x27220A95;
-	return hash;
-}
-
-enum IRCColor formatColor(const char *str) {
-	if (!str) return IRCDefault;
-	uint32_t hash = 0;
-	for (; str[0]; ++str) {
-		hash = hashChar(hash, str[0]);
-	}
-	while (IRCBlack == (hash & IRCLightGray)) {
-		hash = hashChar(hash, '\0');
-	}
-	return (hash & IRCLightGray);
-}
-
 void formatReset(struct Format *format) {
 	format->bold = false;
 	format->italic = false;
diff --git a/handle.c b/handle.c
index 579c073..a46c149 100644
--- a/handle.c
+++ b/handle.c
@@ -160,7 +160,7 @@ static void handleReplyWhoisUser(char *prefix, char *params) {
 		prefix, NULL, NULL, NULL,
 		params, 6, 0, NULL, &nick, &user, &host, NULL, &real
 	);
-	whoisColor = formatColor(user[0] == '~' ? &user[1] : user);
+	whoisColor = colorGen(user[0] == '~' ? &user[1] : user);
 	uiFmt(
 		TagStatus, UIWarm,
 		"\3%d%s\3 is %s@%s, \"%s\"",
@@ -214,7 +214,7 @@ static void handleErrorNoSuchNick(char *prefix, char *params) {
 static void handleJoin(char *prefix, char *params) {
 	char *nick, *user, *chan;
 	parse(prefix, &nick, &user, NULL, params, 1, 0, &chan);
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 
 	if (!strcmp(nick, self.nick)) {
 		tabTouch(TagNone, chan);
@@ -226,7 +226,7 @@ static void handleJoin(char *prefix, char *params) {
 	uiFmt(
 		tag, UICold,
 		"\3%d%s\3 arrives in \3%d%s\3",
-		formatColor(user), nick, formatColor(chan), chan
+		colorGen(user), nick, colorGen(chan), chan
 	);
 	logFmt(tag, NULL, "%s arrives in %s", nick, chan);
 }
@@ -234,7 +234,7 @@ static void handleJoin(char *prefix, char *params) {
 static void handlePart(char *prefix, char *params) {
 	char *nick, *user, *chan, *mesg;
 	parse(prefix, &nick, &user, NULL, params, 1, 1, &chan, &mesg);
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 
 	if (!strcmp(nick, self.nick)) {
 		tabClear(tag);
@@ -247,14 +247,14 @@ static void handlePart(char *prefix, char *params) {
 		uiFmt(
 			tag, UICold,
 			"\3%d%s\3 leaves \3%d%s\3, \"%s\"",
-			formatColor(user), nick, formatColor(chan), chan, dequote(mesg)
+			colorGen(user), nick, colorGen(chan), chan, dequote(mesg)
 		);
 		logFmt(tag, NULL, "%s leaves %s, \"%s\"", nick, chan, dequote(mesg));
 	} else {
 		uiFmt(
 			tag, UICold,
 			"\3%d%s\3 leaves \3%d%s\3",
-			formatColor(user), nick, formatColor(chan), chan
+			colorGen(user), nick, colorGen(chan), chan
 		);
 		logFmt(tag, NULL, "%s leaves %s", nick, chan);
 	}
@@ -263,7 +263,7 @@ static void handlePart(char *prefix, char *params) {
 static void handleKick(char *prefix, char *params) {
 	char *nick, *user, *chan, *kick, *mesg;
 	parse(prefix, &nick, &user, NULL, params, 2, 1, &chan, &kick, &mesg);
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 	bool kicked = !strcmp(kick, self.nick);
 
 	if (kicked) {
@@ -277,9 +277,9 @@ static void handleKick(char *prefix, char *params) {
 		uiFmt(
 			tag, (kicked ? UIHot : UICold),
 			"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"",
-			formatColor(user), nick,
-			formatColor(kick), kick,
-			formatColor(chan), chan,
+			colorGen(user), nick,
+			colorGen(kick), kick,
+			colorGen(chan), chan,
 			dequote(mesg)
 		);
 		logFmt(
@@ -290,9 +290,9 @@ static void handleKick(char *prefix, char *params) {
 		uiFmt(
 			tag, (kicked ? UIHot : UICold),
 			"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3",
-			formatColor(user), nick,
-			formatColor(kick), kick,
-			formatColor(chan), chan
+			colorGen(user), nick,
+			colorGen(kick), kick,
+			colorGen(chan), chan
 		);
 		logFmt(tag, NULL, "%s kicks %s out of %s", nick, kick, chan);
 	}
@@ -311,11 +311,11 @@ static void handleQuit(char *prefix, char *params) {
 			uiFmt(
 				tag, UICold,
 				"\3%d%s\3 leaves, \"%s\"",
-				formatColor(user), nick, dequote(mesg)
+				colorGen(user), nick, dequote(mesg)
 			);
 			logFmt(tag, NULL, "%s leaves, \"%s\"", nick, dequote(mesg));
 		} else {
-			uiFmt(tag, UICold, "\3%d%s\3 leaves", formatColor(user), nick);
+			uiFmt(tag, UICold, "\3%d%s\3 leaves", colorGen(user), nick);
 			logFmt(tag, NULL, "%s leaves", nick);
 		}
 	}
@@ -324,13 +324,13 @@ static void handleQuit(char *prefix, char *params) {
 static void handleReplyTopic(char *prefix, char *params) {
 	char *chan, *topic;
 	parse(prefix, NULL, NULL, NULL, params, 3, 0, NULL, &chan, &topic);
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 
 	urlScan(tag, topic);
 	uiFmt(
 		tag, UICold,
 		"The sign in \3%d%s\3 reads, \"%s\"",
-		formatColor(chan), chan, topic
+		colorGen(chan), chan, topic
 	);
 	logFmt(tag, NULL, "The sign in %s reads, \"%s\"", chan, topic);
 }
@@ -338,7 +338,7 @@ static void handleReplyTopic(char *prefix, char *params) {
 static void handleTopic(char *prefix, char *params) {
 	char *nick, *user, *chan, *topic;
 	parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &topic);
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 
 	if (strcmp(nick, self.nick)) tabTouch(tag, nick);
 
@@ -346,7 +346,7 @@ static void handleTopic(char *prefix, char *params) {
 	uiFmt(
 		tag, UICold,
 		"\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"",
-		formatColor(user), nick, formatColor(chan), chan, topic
+		colorGen(user), nick, colorGen(chan), chan, topic
 	);
 	logFmt(tag, NULL, "%s places a new sign in %s, \"%s\"", nick, chan, topic);
 }
@@ -369,7 +369,7 @@ static void handleReplyWho(char *prefix, char *params) {
 		params, 6, 0, NULL, &chan, &user, NULL, NULL, &nick
 	);
 	if (user[0] == '~') user = &user[1];
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 
 	tabAdd(tag, nick);
 
@@ -377,7 +377,7 @@ static void handleReplyWho(char *prefix, char *params) {
 	int len = snprintf(
 		&who.buf[who.len], cap,
 		"%s\3%d%s\3",
-		(who.len ? ", " : ""), formatColor(user), nick
+		(who.len ? ", " : ""), colorGen(user), nick
 	);
 	if ((size_t)len < cap) who.len += len;
 }
@@ -385,12 +385,12 @@ static void handleReplyWho(char *prefix, char *params) {
 static void handleReplyEndOfWho(char *prefix, char *params) {
 	char *chan;
 	parse(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &chan);
-	struct Tag tag = tagFor(chan, formatColor(chan));
+	struct Tag tag = colorTag(tagFor(chan), chan);
 
 	uiFmt(
 		tag, UICold,
 		"In \3%d%s\3 are %s",
-		formatColor(chan), chan, who.buf
+		colorGen(chan), chan, who.buf
 	);
 	who.len = 0;
 }
@@ -413,7 +413,7 @@ static void handleNick(char *prefix, char *params) {
 		uiFmt(
 			tag, UICold,
 			"\3%d%s\3 is now known as \3%d%s\3",
-			formatColor(user), prev, formatColor(user), next
+			colorGen(user), prev, colorGen(user), next
 		);
 		logFmt(tag, NULL, "%s is now known as %s", prev, next);
 	}
@@ -432,7 +432,7 @@ static void handleCTCP(struct Tag tag, char *nick, char *user, char *mesg) {
 	uiFmt(
 		tag, (ping ? UIHot : UIWarm),
 		"%c\3%d* %s\17 %s",
-		ping["\17\26"], formatColor(user), nick, params
+		ping["\17\26"], colorGen(user), nick, params
 	);
 	logFmt(tag, NULL, "* %s %s", nick, params);
 }
@@ -441,9 +441,8 @@ static void handlePrivmsg(char *prefix, char *params) {
 	char *nick, *user, *chan, *mesg;
 	parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
 	bool direct = !strcmp(chan, self.nick);
-	struct Tag tag = direct
-		? tagFor(nick, formatColor(user))
-		: tagFor(chan, formatColor(chan));
+	struct Tag tag = tagFor(direct ? nick : chan);
+	colorTag(tag, direct ? user : chan);
 	if (mesg[0] == '\1') {
 		handleCTCP(tag, nick, user, mesg);
 		return;
@@ -459,7 +458,7 @@ static void handlePrivmsg(char *prefix, char *params) {
 		tag, (hot ? UIHot : UIWarm),
 		"%c%c\3%d<%s>%c %s",
 		(me ? IRCUnderline : IRCColor), (ping ? IRCReverse : IRCColor),
-		formatColor(user), nick, IRCReset, mesg
+		colorGen(user), nick, IRCReset, mesg
 	);
 	logFmt(tag, NULL, "<%s> %s", nick, mesg);
 }
@@ -467,11 +466,11 @@ static void handlePrivmsg(char *prefix, char *params) {
 static void handleNotice(char *prefix, char *params) {
 	char *nick, *user, *chan, *mesg;
 	parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
+	bool direct = !strcmp(chan, self.nick);
 	struct Tag tag = TagStatus;
 	if (user) {
-		tag = strcmp(chan, self.nick)
-			? tagFor(chan, formatColor(chan))
-			: tagFor(nick, formatColor(user));
+		tag = tagFor(direct ? nick : chan);
+		colorTag(tag, direct ? user : chan);
 	}
 
 	if (strcmp(nick, self.nick)) tabTouch(tag, nick);
@@ -481,7 +480,7 @@ static void handleNotice(char *prefix, char *params) {
 	uiFmt(
 		tag, (ping ? UIHot : UIWarm),
 		"%c\3%d-%s-\17 %s",
-		ping["\17\26"], formatColor(user), nick, mesg
+		ping["\17\26"], colorGen(user), nick, mesg
 	);
 	logFmt(tag, NULL, "-%s- %s", nick, mesg);
 }
diff --git a/input.c b/input.c
index f570dee..45de9f1 100644
--- a/input.c
+++ b/input.c
@@ -66,8 +66,8 @@ static void inputQuery(struct Tag tag, char *params) {
 	char *nick = strsep(&params, " ");
 	if (nick) {
 		tabTouch(TagNone, nick);
-		uiShowTag(tagFor(nick, IRCDefault));
-		logReplay(tagFor(nick, IRCDefault));
+		uiShowTag(tagFor(nick));
+		logReplay(tagFor(nick));
 	} else {
 		uiLog(tag, UIHot, L"/query requires a nickname");
 	}
diff --git a/tag.c b/tag.c
index 649ac49..5b4232e 100644
--- a/tag.c
+++ b/tag.c
@@ -23,37 +23,31 @@
 
 static struct {
 	char *name[TagsLen];
-	enum IRCColor color[TagsLen];
 	size_t len;
 } tags = {
 	.name = { "<none>", "<status>", "<raw>" },
-	.color = { IRCBlack, IRCDefault, IRCRed },
 	.len = 3,
 };
 
-const struct Tag TagNone   = { 0, "<none>", IRCBlack };
-const struct Tag TagStatus = { 1, "<status>", IRCDefault };
-const struct Tag TagRaw    = { 2, "<raw>", IRCRed };
+const struct Tag TagNone   = { 0, "<none>" };
+const struct Tag TagStatus = { 1, "<status>" };
+const struct Tag TagRaw    = { 2, "<raw>" };
 
 struct Tag tagFind(const char *name) {
 	for (size_t id = 0; id < tags.len; ++id) {
 		if (strcmp(tags.name[id], name)) continue;
-		return (struct Tag) { id, tags.name[id], tags.color[id] };
+		return (struct Tag) { id, tags.name[id] };
 	}
 	return TagNone;
 }
 
-struct Tag tagFor(const char *name, enum IRCColor color) {
+struct Tag tagFor(const char *name) {
 	struct Tag tag = tagFind(name);
-	if (tag.id != TagNone.id) {
-		tag.color = tags.color[tag.id] = color;
-		return tag;
-	}
+	if (tag.id != TagNone.id) return tag;
 	if (tags.len == TagsLen) return TagStatus;
 
 	size_t id = tags.len++;
 	tags.name[id] = strdup(name);
-	tags.color[id] = color;
 	if (!tags.name[id]) err(EX_OSERR, "strdup");
-	return (struct Tag) { id, tags.name[id], color };
+	return (struct Tag) { id, tags.name[id] };
 }
diff --git a/ui.c b/ui.c
index 67f35c0..fb4a366 100644
--- a/ui.c
+++ b/ui.c
@@ -339,10 +339,10 @@ static void uiStatus(void) {
 		wchar_t *str;
 		int len = aswprintf(
 			&str, L"%c\3%d %d %s %n(\3%02d%u\3%d) ",
-			(windows.active == win ? IRCReverse : IRCReset), win->tag.color,
+			(windows.active == win ? IRCReverse : IRCReset), colorFor(win->tag),
 			num, win->tag.name,
-			&unread, (win->hot ? IRCWhite : win->tag.color), win->unread,
-			win->tag.color
+			&unread, (win->hot ? IRCWhite : colorFor(win->tag)), win->unread,
+			colorFor(win->tag)
 		);
 		if (len < 0) err(EX_OSERR, "aswprintf");
 		if (!win->unread) str[unread] = L'\0';
@@ -557,7 +557,7 @@ void uiPrompt(bool nickChanged) {
 	if (nickChanged || !promptMesg || !promptAction) {
 		free(promptMesg);
 		free(promptAction);
-		enum IRCColor color = formatColor(self.user);
+		enum IRCColor color = colorGen(self.user);
 		int len = aswprintf(&promptMesg, L"\3%d<%s>\3 ", color, self.nick);
 		if (len < 0) err(EX_OSERR, "aswprintf");
 		len = aswprintf(&promptAction, L"\3%d* %s\3 ", color, self.nick);