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 + * + * 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 . + */ + +#include + +#include "chat.h" + +// Adapted from . +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 . -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(¶ms, " "); 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 = { "", "", "" }, - .color = { IRCBlack, IRCDefault, IRCRed }, .len = 3, }; -const struct Tag TagNone = { 0, "", IRCBlack }; -const struct Tag TagStatus = { 1, "", IRCDefault }; -const struct Tag TagRaw = { 2, "", IRCRed }; +const struct Tag TagNone = { 0, "" }; +const struct Tag TagStatus = { 1, "" }; +const struct Tag TagRaw = { 2, "" }; 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);