Move nick and tag coloring to color.c

master
Curtis McEnroe 2019-02-25 23:10:40 -05:00
parent b2f6082dff
commit 4cda410b57
No known key found for this signature in database
GPG Key ID: CEA2F97ADCFCD77C
10 changed files with 117 additions and 88 deletions

View File

@ -15,6 +15,7 @@ MAN1 = catgirl.1
-include config.mk -include config.mk
OBJS += chat.o OBJS += chat.o
OBJS += color.o
OBJS += edit.o OBJS += edit.o
OBJS += event.o OBJS += event.o
OBJS += format.o OBJS += format.o

3
README
View File

@ -39,6 +39,7 @@ FILES
input.c input command handling input.c input command handling
irc.c TLS client connection irc.c TLS client connection
format.c IRC formatting format.c IRC formatting
color.c nick and channel coloring
ui.c cursed UI ui.c cursed UI
term.c terminal features unsupported by curses term.c terminal features unsupported by curses
edit.c line editing edit.c line editing
@ -53,4 +54,4 @@ FILES
SEE ALSO SEE ALSO
catgirl(1), sandman(1) catgirl(1), sandman(1)
Causal Agency January 25, 2019 Causal Agency Causal Agency February 25, 2019 Causal Agency

View File

@ -1,4 +1,4 @@
.Dd January 25, 2019 .Dd February 25, 2019
.Dt CATGIRL 7 .Dt CATGIRL 7
.Os "Causal Agency" .Os "Causal Agency"
. .
@ -79,6 +79,8 @@ input command handling
TLS client connection TLS client connection
.It Pa format.c .It Pa format.c
IRC formatting IRC formatting
.It Pa color.c
nick and channel coloring
.It Pa ui.c .It Pa ui.c
cursed UI cursed UI
.It Pa term.c .It Pa term.c

30
chat.h
View File

@ -52,6 +52,18 @@ void eventWait(const char *argv[static 2]);
void eventPipe(const char *argv[static 2]); void eventPipe(const char *argv[static 2]);
noreturn void eventLoop(void); 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 { enum IRCColor {
IRCWhite, IRCWhite,
IRCBlack, IRCBlack,
@ -80,19 +92,6 @@ enum {
IRCUnderline = 037, 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 { struct Format {
const wchar_t *str; const wchar_t *str;
size_t len; size_t len;
@ -102,7 +101,10 @@ struct Format {
}; };
void formatReset(struct Format *format); void formatReset(struct Format *format);
bool formatParse(struct Format *format, const wchar_t *split); 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 handle(char *line);
void input(struct Tag tag, char *line); void input(struct Tag tag, char *line);

50
color.c 100644
View File

@ -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;
}

View File

@ -21,26 +21,6 @@
#include "chat.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 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) { void formatReset(struct Format *format) {
format->bold = false; format->bold = false;
format->italic = false; format->italic = false;

View File

@ -160,7 +160,7 @@ static void handleReplyWhoisUser(char *prefix, char *params) {
prefix, NULL, NULL, NULL, prefix, NULL, NULL, NULL,
params, 6, 0, NULL, &nick, &user, &host, NULL, &real params, 6, 0, NULL, &nick, &user, &host, NULL, &real
); );
whoisColor = formatColor(user[0] == '~' ? &user[1] : user); whoisColor = colorGen(user[0] == '~' ? &user[1] : user);
uiFmt( uiFmt(
TagStatus, UIWarm, TagStatus, UIWarm,
"\3%d%s\3 is %s@%s, \"%s\"", "\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) { static void handleJoin(char *prefix, char *params) {
char *nick, *user, *chan; char *nick, *user, *chan;
parse(prefix, &nick, &user, NULL, params, 1, 0, &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)) { if (!strcmp(nick, self.nick)) {
tabTouch(TagNone, chan); tabTouch(TagNone, chan);
@ -226,7 +226,7 @@ static void handleJoin(char *prefix, char *params) {
uiFmt( uiFmt(
tag, UICold, tag, UICold,
"\3%d%s\3 arrives in \3%d%s\3", "\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); 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) { static void handlePart(char *prefix, char *params) {
char *nick, *user, *chan, *mesg; char *nick, *user, *chan, *mesg;
parse(prefix, &nick, &user, NULL, params, 1, 1, &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)) { if (!strcmp(nick, self.nick)) {
tabClear(tag); tabClear(tag);
@ -247,14 +247,14 @@ static void handlePart(char *prefix, char *params) {
uiFmt( uiFmt(
tag, UICold, tag, UICold,
"\3%d%s\3 leaves \3%d%s\3, \"%s\"", "\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)); logFmt(tag, NULL, "%s leaves %s, \"%s\"", nick, chan, dequote(mesg));
} else { } else {
uiFmt( uiFmt(
tag, UICold, tag, UICold,
"\3%d%s\3 leaves \3%d%s\3", "\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); 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) { static void handleKick(char *prefix, char *params) {
char *nick, *user, *chan, *kick, *mesg; char *nick, *user, *chan, *kick, *mesg;
parse(prefix, &nick, &user, NULL, params, 2, 1, &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); bool kicked = !strcmp(kick, self.nick);
if (kicked) { if (kicked) {
@ -277,9 +277,9 @@ static void handleKick(char *prefix, char *params) {
uiFmt( uiFmt(
tag, (kicked ? UIHot : UICold), tag, (kicked ? UIHot : UICold),
"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"", "\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"",
formatColor(user), nick, colorGen(user), nick,
formatColor(kick), kick, colorGen(kick), kick,
formatColor(chan), chan, colorGen(chan), chan,
dequote(mesg) dequote(mesg)
); );
logFmt( logFmt(
@ -290,9 +290,9 @@ static void handleKick(char *prefix, char *params) {
uiFmt( uiFmt(
tag, (kicked ? UIHot : UICold), tag, (kicked ? UIHot : UICold),
"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3", "\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3",
formatColor(user), nick, colorGen(user), nick,
formatColor(kick), kick, colorGen(kick), kick,
formatColor(chan), chan colorGen(chan), chan
); );
logFmt(tag, NULL, "%s kicks %s out of %s", nick, kick, 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( uiFmt(
tag, UICold, tag, UICold,
"\3%d%s\3 leaves, \"%s\"", "\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)); logFmt(tag, NULL, "%s leaves, \"%s\"", nick, dequote(mesg));
} else { } 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); logFmt(tag, NULL, "%s leaves", nick);
} }
} }
@ -324,13 +324,13 @@ static void handleQuit(char *prefix, char *params) {
static void handleReplyTopic(char *prefix, char *params) { static void handleReplyTopic(char *prefix, char *params) {
char *chan, *topic; char *chan, *topic;
parse(prefix, NULL, NULL, NULL, params, 3, 0, NULL, &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); urlScan(tag, topic);
uiFmt( uiFmt(
tag, UICold, tag, UICold,
"The sign in \3%d%s\3 reads, \"%s\"", "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); 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) { static void handleTopic(char *prefix, char *params) {
char *nick, *user, *chan, *topic; char *nick, *user, *chan, *topic;
parse(prefix, &nick, &user, NULL, params, 2, 0, &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); if (strcmp(nick, self.nick)) tabTouch(tag, nick);
@ -346,7 +346,7 @@ static void handleTopic(char *prefix, char *params) {
uiFmt( uiFmt(
tag, UICold, tag, UICold,
"\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"", "\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); 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 params, 6, 0, NULL, &chan, &user, NULL, NULL, &nick
); );
if (user[0] == '~') user = &user[1]; if (user[0] == '~') user = &user[1];
struct Tag tag = tagFor(chan, formatColor(chan)); struct Tag tag = colorTag(tagFor(chan), chan);
tabAdd(tag, nick); tabAdd(tag, nick);
@ -377,7 +377,7 @@ static void handleReplyWho(char *prefix, char *params) {
int len = snprintf( int len = snprintf(
&who.buf[who.len], cap, &who.buf[who.len], cap,
"%s\3%d%s\3", "%s\3%d%s\3",
(who.len ? ", " : ""), formatColor(user), nick (who.len ? ", " : ""), colorGen(user), nick
); );
if ((size_t)len < cap) who.len += len; 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) { static void handleReplyEndOfWho(char *prefix, char *params) {
char *chan; char *chan;
parse(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &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( uiFmt(
tag, UICold, tag, UICold,
"In \3%d%s\3 are %s", "In \3%d%s\3 are %s",
formatColor(chan), chan, who.buf colorGen(chan), chan, who.buf
); );
who.len = 0; who.len = 0;
} }
@ -413,7 +413,7 @@ static void handleNick(char *prefix, char *params) {
uiFmt( uiFmt(
tag, UICold, tag, UICold,
"\3%d%s\3 is now known as \3%d%s\3", "\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); 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( uiFmt(
tag, (ping ? UIHot : UIWarm), tag, (ping ? UIHot : UIWarm),
"%c\3%d* %s\17 %s", "%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); logFmt(tag, NULL, "* %s %s", nick, params);
} }
@ -441,9 +441,8 @@ static void handlePrivmsg(char *prefix, char *params) {
char *nick, *user, *chan, *mesg; char *nick, *user, *chan, *mesg;
parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg); parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
bool direct = !strcmp(chan, self.nick); bool direct = !strcmp(chan, self.nick);
struct Tag tag = direct struct Tag tag = tagFor(direct ? nick : chan);
? tagFor(nick, formatColor(user)) colorTag(tag, direct ? user : chan);
: tagFor(chan, formatColor(chan));
if (mesg[0] == '\1') { if (mesg[0] == '\1') {
handleCTCP(tag, nick, user, mesg); handleCTCP(tag, nick, user, mesg);
return; return;
@ -459,7 +458,7 @@ static void handlePrivmsg(char *prefix, char *params) {
tag, (hot ? UIHot : UIWarm), tag, (hot ? UIHot : UIWarm),
"%c%c\3%d<%s>%c %s", "%c%c\3%d<%s>%c %s",
(me ? IRCUnderline : IRCColor), (ping ? IRCReverse : IRCColor), (me ? IRCUnderline : IRCColor), (ping ? IRCReverse : IRCColor),
formatColor(user), nick, IRCReset, mesg colorGen(user), nick, IRCReset, mesg
); );
logFmt(tag, NULL, "<%s> %s", nick, 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) { static void handleNotice(char *prefix, char *params) {
char *nick, *user, *chan, *mesg; char *nick, *user, *chan, *mesg;
parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg); parse(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
bool direct = !strcmp(chan, self.nick);
struct Tag tag = TagStatus; struct Tag tag = TagStatus;
if (user) { if (user) {
tag = strcmp(chan, self.nick) tag = tagFor(direct ? nick : chan);
? tagFor(chan, formatColor(chan)) colorTag(tag, direct ? user : chan);
: tagFor(nick, formatColor(user));
} }
if (strcmp(nick, self.nick)) tabTouch(tag, nick); if (strcmp(nick, self.nick)) tabTouch(tag, nick);
@ -481,7 +480,7 @@ static void handleNotice(char *prefix, char *params) {
uiFmt( uiFmt(
tag, (ping ? UIHot : UIWarm), tag, (ping ? UIHot : UIWarm),
"%c\3%d-%s-\17 %s", "%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); logFmt(tag, NULL, "-%s- %s", nick, mesg);
} }

View File

@ -66,8 +66,8 @@ static void inputQuery(struct Tag tag, char *params) {
char *nick = strsep(&params, " "); char *nick = strsep(&params, " ");
if (nick) { if (nick) {
tabTouch(TagNone, nick); tabTouch(TagNone, nick);
uiShowTag(tagFor(nick, IRCDefault)); uiShowTag(tagFor(nick));
logReplay(tagFor(nick, IRCDefault)); logReplay(tagFor(nick));
} else { } else {
uiLog(tag, UIHot, L"/query requires a nickname"); uiLog(tag, UIHot, L"/query requires a nickname");
} }

20
tag.c
View File

@ -23,37 +23,31 @@
static struct { static struct {
char *name[TagsLen]; char *name[TagsLen];
enum IRCColor color[TagsLen];
size_t len; size_t len;
} tags = { } tags = {
.name = { "<none>", "<status>", "<raw>" }, .name = { "<none>", "<status>", "<raw>" },
.color = { IRCBlack, IRCDefault, IRCRed },
.len = 3, .len = 3,
}; };
const struct Tag TagNone = { 0, "<none>", IRCBlack }; const struct Tag TagNone = { 0, "<none>" };
const struct Tag TagStatus = { 1, "<status>", IRCDefault }; const struct Tag TagStatus = { 1, "<status>" };
const struct Tag TagRaw = { 2, "<raw>", IRCRed }; const struct Tag TagRaw = { 2, "<raw>" };
struct Tag tagFind(const char *name) { struct Tag tagFind(const char *name) {
for (size_t id = 0; id < tags.len; ++id) { for (size_t id = 0; id < tags.len; ++id) {
if (strcmp(tags.name[id], name)) continue; 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; return TagNone;
} }
struct Tag tagFor(const char *name, enum IRCColor color) { struct Tag tagFor(const char *name) {
struct Tag tag = tagFind(name); struct Tag tag = tagFind(name);
if (tag.id != TagNone.id) { if (tag.id != TagNone.id) return tag;
tag.color = tags.color[tag.id] = color;
return tag;
}
if (tags.len == TagsLen) return TagStatus; if (tags.len == TagsLen) return TagStatus;
size_t id = tags.len++; size_t id = tags.len++;
tags.name[id] = strdup(name); tags.name[id] = strdup(name);
tags.color[id] = color;
if (!tags.name[id]) err(EX_OSERR, "strdup"); if (!tags.name[id]) err(EX_OSERR, "strdup");
return (struct Tag) { id, tags.name[id], color }; return (struct Tag) { id, tags.name[id] };
} }

8
ui.c
View File

@ -339,10 +339,10 @@ static void uiStatus(void) {
wchar_t *str; wchar_t *str;
int len = aswprintf( int len = aswprintf(
&str, L"%c\3%d %d %s %n(\3%02d%u\3%d) ", &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, num, win->tag.name,
&unread, (win->hot ? IRCWhite : win->tag.color), win->unread, &unread, (win->hot ? IRCWhite : colorFor(win->tag)), win->unread,
win->tag.color colorFor(win->tag)
); );
if (len < 0) err(EX_OSERR, "aswprintf"); if (len < 0) err(EX_OSERR, "aswprintf");
if (!win->unread) str[unread] = L'\0'; if (!win->unread) str[unread] = L'\0';
@ -557,7 +557,7 @@ void uiPrompt(bool nickChanged) {
if (nickChanged || !promptMesg || !promptAction) { if (nickChanged || !promptMesg || !promptAction) {
free(promptMesg); free(promptMesg);
free(promptAction); 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); int len = aswprintf(&promptMesg, L"\3%d<%s>\3 ", color, self.nick);
if (len < 0) err(EX_OSERR, "aswprintf"); if (len < 0) err(EX_OSERR, "aswprintf");
len = aswprintf(&promptAction, L"\3%d* %s\3 ", color, self.nick); len = aswprintf(&promptAction, L"\3%d* %s\3 ", color, self.nick);