diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | README.7 | 21 | ||||
-rw-r--r-- | catgirl.1 | 23 | ||||
-rw-r--r-- | chat.c | 16 | ||||
-rw-r--r-- | chat.h | 45 | ||||
-rw-r--r-- | command.c | 69 | ||||
-rw-r--r-- | complete.c (renamed from cache.c) | 140 | ||||
-rwxr-xr-x | configure | 1 | ||||
-rw-r--r-- | handle.c | 133 | ||||
-rw-r--r-- | input.c | 12 | ||||
-rw-r--r-- | sandman.1 (renamed from scripts/sandman.1) | 0 | ||||
-rw-r--r-- | sandman.m (renamed from scripts/sandman.m) | 0 | ||||
-rw-r--r-- | scripts/.gitignore | 1 | ||||
-rw-r--r-- | scripts/Makefile | 22 | ||||
-rw-r--r-- | ui.c | 6 | ||||
-rw-r--r-- | window.c | 9 |
17 files changed, 239 insertions, 284 deletions
diff --git a/.gitignore b/.gitignore index b31d1c5..519791d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ catgirl chroot.tar config.mk root/ +sandman tags diff --git a/Makefile b/Makefile index e8ef63a..66fb408 100644 --- a/Makefile +++ b/Makefile @@ -8,14 +8,18 @@ CFLAGS += ${CEXTS:%=-Wno-%} LDADD.libtls = -ltls LDADD.ncursesw = -lncursesw +BINS = catgirl +MANS = ${BINS:=.1} + -include config.mk LDLIBS = ${LDADD.libtls} ${LDADD.ncursesw} +LDLIBS.sandman = -framework Cocoa OBJS += buffer.o -OBJS += cache.o OBJS += chat.o OBJS += command.o +OBJS += complete.o OBJS += config.o OBJS += edit.o OBJS += filter.o @@ -28,11 +32,13 @@ OBJS += url.o OBJS += window.o OBJS += xdg.o +OBJS.sandman = sandman.o + TESTS += edit.t dev: tags all check -all: catgirl +all: ${BINS} catgirl: ${OBJS} ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ @@ -41,6 +47,9 @@ ${OBJS}: chat.h edit.o edit.t input.o: edit.h +sandman: ${OBJS.sandman} + ${CC} ${LDFLAGS} ${OBJS.$@} ${LDLIBS.$@} -o $@ + check: ${TESTS} .SUFFIXES: .t @@ -53,15 +62,16 @@ tags: *.[ch] ctags -w *.[ch] clean: - rm -f catgirl ${OBJS} ${TESTS} tags + rm -f ${BINS} ${OBJS} ${OBJS.sandman} ${TESTS} tags -install: catgirl catgirl.1 +install: ${BINS} ${MANS} install -d ${DESTDIR}${BINDIR} ${DESTDIR}${MANDIR}/man1 - install catgirl ${DESTDIR}${BINDIR} - install -m 644 catgirl.1 ${DESTDIR}${MANDIR}/man1 + install ${BINS} ${DESTDIR}${BINDIR} + install -m 644 ${MANS} ${DESTDIR}${MANDIR}/man1 uninstall: - rm -f ${DESTDIR}${BINDIR}/catgirl ${DESTDIR}${MANDIR}/man1/catgirl.1 + rm -f ${BINS:%=${DESTDIR}${BINDIR}/%} + rm -f ${MANS:%=${DESTDIR}${MANDIR}/man1/%} CHROOT_USER = chat CHROOT_GROUP = ${CHROOT_USER} diff --git a/README.7 b/README.7 index 64024ab..a26d270 100644 --- a/README.7 +++ b/README.7 @@ -1,5 +1,5 @@ .\" To view this file: $ man ./README.7 -.Dd July 30, 2022 +.Dd July 9, 2023 .Dt README 7 .Os "Causal Agency" . @@ -173,10 +173,10 @@ wrapper is provided for macOS to stop and start .Nm on system sleep and wake. -Install it as follows: +To enable it, +configure with: .Bd -literal -offset indent -$ make -C scripts sandman -# make -C scripts install +$ ./configure --enable-sandman .Ed . .Sh FILES @@ -201,8 +201,8 @@ command handling line wrapping .It Pa edit.c line editing -.It Pa cache.c -ordered cache +.It Pa complete.c +tab complete .It Pa url.c URL detection .It Pa filter.c @@ -213,6 +213,8 @@ chat logging configuration parsing .It Pa xdg.c XDG base directories +.It Pa sandman.m +sleep/wake wrapper for macOS .El . .Pp @@ -222,8 +224,6 @@ example .Xr tmux 1 configuration for multiple networks and automatic reconnects -.It Pa scripts/sandman.m -sleep/wake wrapper for macOS .It Pa scripts/notify-send.scpt .Xr notify-send 1 in AppleScript @@ -257,14 +257,15 @@ Monetary contributions can be .Lk https://liberapay.com/june/donate "donated via Liberapay" . . .Sh SEE ALSO -.Xr catgirl 1 +.Xr catgirl 1 , +.Xr sandman 1 . .Pp IRC bouncer: .Lk https://git.causal.agency/pounce "pounce" . .Rs -.%A June Bug +.%A June McEnroe .%T IRC Suite .%U https://text.causal.agency/010-irc-suite.txt .%D June 19, 2020 diff --git a/catgirl.1 b/catgirl.1 index 32eb365..815eade 100644 --- a/catgirl.1 +++ b/catgirl.1 @@ -1,4 +1,4 @@ -.Dd May 29, 2022 +.Dd October 11, 2023 .Dt CATGIRL 1 .Os . @@ -8,7 +8,7 @@ . .Sh SYNOPSIS .Nm -.Op Fl KRelqv +.Op Fl Relqv .Op Fl C Ar copy .Op Fl H Ar hash .Op Fl I Ar highlight @@ -145,22 +145,6 @@ The commands which can be matched are: .Sy QUIT , .Sy SETNAME . . -.It Fl K | Cm kiosk -Disable the -.Ic /copy , -.Ic /debug , -.Ic /exec , -.Ic /join , -.Ic /list , -.Ic /msg , -.Ic /open , -.Ic /part , -.Ic /query , -.Ic /quote -commands. -Replace the username -with a hash of its original value. -. .It Fl N Ar util | Cm notify No = Ar util Send notifications using a utility. Subsequent @@ -649,6 +633,9 @@ use the option. .It Ic /move Oo Ar name Oc Ar num Move the named or current window to number. +.It Ic /o ... +Alias of +.Ic /open . .It Ic /open Op Ar count Open each of .Ar count diff --git a/chat.c b/chat.c index 39b1a93..6728240 100644 --- a/chat.c +++ b/chat.c @@ -245,7 +245,6 @@ int main(int argc, char *argv[]) { { .val = 'C', .name = "copy", required_argument }, { .val = 'H', .name = "hash", required_argument }, { .val = 'I', .name = "highlight", required_argument }, - { .val = 'K', .name = "kiosk", no_argument }, { .val = 'N', .name = "notify", required_argument }, { .val = 'O', .name = "open", required_argument }, { .val = 'R', .name = "restrict", no_argument }, @@ -286,7 +285,6 @@ int main(int argc, char *argv[]) { break; case 'C': utilPush(&urlCopyUtil, optarg); break; case 'H': parseHash(optarg); break; case 'I': filterAdd(Hot, optarg); - break; case 'K': self.kiosk = true; break; case 'N': utilPush(&uiNotifyUtil, optarg); break; case 'O': utilPush(&urlOpenUtil, optarg); break; case 'R': self.restricted = true; @@ -341,13 +339,6 @@ int main(int argc, char *argv[]) { if (!user) user = self.nicks[0]; if (!real) real = self.nicks[0]; - if (self.kiosk) { - char *hash; - int n = asprintf(&hash, "%08" PRIx32, _hash(user)); - if (n < 0) err(EX_OSERR, "asprintf"); - user = hash; - } - if (pass && !pass[0]) { char *buf = malloc(512); if (!buf) err(EX_OSERR, "malloc"); @@ -374,7 +365,7 @@ int main(int argc, char *argv[]) { set(&network.name, host); set(&self.nick, "*"); - inputCache(); + inputCompletion(); ircConfig(insecure, trust, cert, priv); @@ -418,8 +409,7 @@ int main(int argc, char *argv[]) { signal(SIGTERM, signalHandler); signal(SIGCHLD, signalHandler); - bool pipes = !self.kiosk && !self.restricted; - if (pipes) { + if (!self.restricted) { int error = pipe(utilPipe) || pipe(execPipe); if (error) err(EX_OSERR, "pipe"); @@ -437,7 +427,7 @@ int main(int argc, char *argv[]) { { .events = POLLIN, .fd = execPipe[0] }, }; while (!self.quit) { - int nfds = poll(fds, (pipes ? ARRAY_LEN(fds) : 2), -1); + int nfds = poll(fds, (self.restricted ? 2 : ARRAY_LEN(fds)), -1); if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll"); if (nfds > 0) { if (fds[0].revents) inputRead(); diff --git a/chat.h b/chat.h index 628d416..2a41cf6 100644 --- a/chat.h +++ b/chat.h @@ -168,6 +168,19 @@ extern struct Network { char invex; } network; +static inline uint prefixBit(char p) { + char *s = strchr(network.prefixes, p); + if (!s) return 0; + return 1 << (s - network.prefixes); +} + +static inline char bitPrefix(uint p) { + for (uint i = 0; network.prefixes[i]; ++i) { + if (p & (1 << i)) return network.prefixes[i]; + } + return '\0'; +} + #define ENUM_CAP \ X("causal.agency/consumer", CapConsumer) \ X("chghost", CapChghost) \ @@ -189,7 +202,6 @@ enum Cap { extern struct Self { bool debug; - bool kiosk; bool restricted; size_t pos; enum Cap caps; @@ -278,7 +290,6 @@ enum Reply { ReplyNamesAuto, ReplyTopic, ReplyTopicAuto, - ReplyWho, ReplyWhois, ReplyWhowas, ReplyCap, @@ -292,7 +303,7 @@ const char *commandIsPrivmsg(uint id, const char *input); const char *commandIsNotice(uint id, const char *input); const char *commandIsAction(uint id, const char *input); size_t commandWillSplit(uint id, const char *input); -void commandCache(void); +void commandCompletion(void); enum Heat { Ice, @@ -334,7 +345,7 @@ void inputWait(void); void inputUpdate(void); bool inputPending(uint id); void inputRead(void); -void inputCache(void); +void inputCompletion(void); int inputSave(FILE *file); void inputLoad(FILE *file, size_t version); @@ -396,24 +407,22 @@ int bufferReflow( struct Buffer *buffer, int cols, enum Heat thresh, size_t tail ); -struct Entry { - enum Color color; - uint prefixBits; -}; struct Cursor { uint gen; struct Node *node; }; -struct Entry *cacheInsert(bool touch, uint id, const char *key); -const struct Entry *cacheGet(uint id, const char *key); -void cacheReplace(bool touch, const char *old, const char *new); -const char *cacheComplete(struct Cursor *curs, uint id, const char *prefix); -const char *cacheSearch(struct Cursor *curs, uint id, const char *substr); -uint cacheID(struct Cursor *curs, const char *key); -void cacheAccept(struct Cursor *curs); -void cacheReject(struct Cursor *curs); -void cacheRemove(uint id, const char *key); -void cacheClear(uint id); +void completePush(uint id, const char *str, enum Color color); +void completePull(uint id, const char *str, enum Color color); +void completeReplace(const char *old, const char *new); +void completeRemove(uint id, const char *str); +enum Color completeColor(uint id, const char *str); +uint *completeBits(uint id, const char *str); +const char *completePrefix(struct Cursor *curs, uint id, const char *prefix); +const char *completeSubstr(struct Cursor *curs, uint id, const char *substr); +const char *completeEach(struct Cursor *curs, uint id); +uint completeEachID(struct Cursor *curs, const char *str); +void completeAccept(struct Cursor *curs); +void completeReject(struct Cursor *curs); extern struct Util urlOpenUtil; extern struct Util urlCopyUtil; diff --git a/command.c b/command.c index 3acbe76..502ff17 100644 --- a/command.c +++ b/command.c @@ -139,7 +139,7 @@ static void commandMsg(uint id, char *params) { char *nick = strsep(¶ms, " "); uint msg = idFor(nick); if (idColors[msg] == Default) { - idColors[msg] = cacheGet(id, nick)->color; + idColors[msg] = completeColor(id, nick); } if (params) { splitMessage("PRIVMSG", msg, params); @@ -219,8 +219,31 @@ static void commandNames(uint id, char *params) { static void commandOps(uint id, char *params) { (void)params; - ircFormat("WHO %s\r\n", idNames[id]); - replies[ReplyWho]++; + char buf[1024]; + char *ptr = buf, *end = &buf[sizeof(buf)]; + ptr = seprintf( + ptr, end, "The council of \3%02d%s\3 are ", + idColors[id], idNames[id] + ); + bool first = true; + struct Cursor curs = {0}; + for (const char *nick; (nick = completeEach(&curs, id));) { + char prefix = bitPrefix(*completeBits(id, nick)); + if (!prefix || prefix == '+') continue; + ptr = seprintf( + ptr, end, "%s\3%02d%c%s\3", + (first ? "" : ", "), completeColor(id, nick), prefix, nick + ); + first = false; + } + if (first) { + uiFormat( + id, Warm, NULL, "\3%02d%s\3 is a lawless wasteland", + idColors[id], idNames[id] + ); + } else { + uiWrite(id, Warm, NULL, buf); + } } static void commandInvite(uint id, char *params) { @@ -380,7 +403,7 @@ static void commandQuery(uint id, char *params) { if (!params) return; uint query = idFor(params); if (idColors[query] == Default) { - idColors[query] = cacheGet(id, params)->color; + idColors[query] = completeColor(id, params); } windowShow(windowFor(query)); } @@ -397,10 +420,10 @@ static void commandWindow(uint id, char *params) { return; } struct Cursor curs = {0}; - for (const char *match; (match = cacheSearch(&curs, None, params));) { - id = idFind(match); + for (const char *str; (str = completeSubstr(&curs, None, params));) { + id = idFind(str); if (!id) continue; - cacheAccept(&curs); + completeAccept(&curs); windowShow(windowFor(id)); break; } @@ -537,7 +560,6 @@ static void commandHelp(uint id, char *params) { enum Flag { BIT(Multiline), BIT(Restrict), - BIT(Kiosk), }; static const struct Handler { @@ -549,37 +571,37 @@ static const struct Handler { { "/away", commandAway, 0, 0 }, { "/ban", commandBan, 0, 0 }, { "/close", commandClose, 0, 0 }, - { "/copy", commandCopy, Restrict | Kiosk, 0 }, + { "/copy", commandCopy, Restrict, 0 }, { "/cs", commandCS, 0, 0 }, - { "/debug", commandDebug, Kiosk, 0 }, + { "/debug", commandDebug, 0, 0 }, { "/deop", commandDeop, 0, 0 }, { "/devoice", commandDevoice, 0, 0 }, { "/except", commandExcept, 0, 0 }, - { "/exec", commandExec, Multiline | Restrict | Kiosk, 0 }, + { "/exec", commandExec, Multiline | Restrict, 0 }, { "/help", commandHelp, 0, 0 }, // Restrict special case. { "/highlight", commandHighlight, 0, 0 }, { "/ignore", commandIgnore, 0, 0 }, { "/invex", commandInvex, 0, 0 }, { "/invite", commandInvite, 0, 0 }, - { "/join", commandJoin, Kiosk, 0 }, + { "/join", commandJoin, 0, 0 }, { "/kick", commandKick, 0, 0 }, - { "/list", commandList, Kiosk, 0 }, + { "/list", commandList, 0, 0 }, { "/me", commandMe, Multiline, 0 }, { "/mode", commandMode, 0, 0 }, { "/move", commandMove, 0, 0 }, - { "/msg", commandMsg, Multiline | Kiosk, 0 }, + { "/msg", commandMsg, Multiline, 0 }, { "/names", commandNames, 0, 0 }, { "/nick", commandNick, 0, 0 }, { "/notice", commandNotice, Multiline, 0 }, { "/ns", commandNS, 0, 0 }, - { "/o", commandOpen, Restrict | Kiosk, 0 }, + { "/o", commandOpen, Restrict, 0 }, { "/op", commandOp, 0, 0 }, - { "/open", commandOpen, Restrict | Kiosk, 0 }, + { "/open", commandOpen, Restrict, 0 }, { "/ops", commandOps, 0, 0 }, - { "/part", commandPart, Kiosk, 0 }, - { "/query", commandQuery, Kiosk, 0 }, + { "/part", commandPart, 0, 0 }, + { "/query", commandQuery, 0, 0 }, { "/quit", commandQuit, 0, 0 }, - { "/quote", commandQuote, Multiline | Kiosk, 0 }, + { "/quote", commandQuote, Multiline, 0 }, { "/say", commandPrivmsg, Multiline, 0 }, { "/setname", commandSetname, 0, CapSetname }, { "/topic", commandTopic, 0, 0 }, @@ -649,7 +671,6 @@ size_t commandWillSplit(uint id, const char *input) { static bool commandAvailable(const struct Handler *handler) { if (handler->flags & Restrict && self.restricted) return false; - if (handler->flags & Kiosk && self.kiosk) return false; if (handler->caps && (handler->caps & self.caps) != handler->caps) { return false; } @@ -672,8 +693,8 @@ void command(uint id, char *input) { struct Cursor curs = {0}; const char *cmd = strsep(&input, " "); - const char *unique = cacheComplete(&curs, None, cmd); - if (unique && !cacheComplete(&curs, None, cmd)) { + const char *unique = completePrefix(&curs, None, cmd); + if (unique && !completePrefix(&curs, None, cmd)) { cmd = unique; } @@ -701,9 +722,9 @@ void command(uint id, char *input) { handler->fn(id, input); } -void commandCache(void) { +void commandCompletion(void) { for (size_t i = 0; i < ARRAY_LEN(Commands); ++i) { if (!commandAvailable(&Commands[i])) continue; - cacheInsert(false, None, Commands[i].cmd); + completePush(None, Commands[i].cmd, Default); } } diff --git a/cache.c b/complete.c index 30ebef4..3552c7c 100644 --- a/cache.c +++ b/complete.c @@ -26,7 +26,6 @@ */ #include <err.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sysexits.h> @@ -35,25 +34,25 @@ struct Node { uint id; - char *key; - struct Entry entry; + char *str; + enum Color color; + uint bits; struct Node *prev; struct Node *next; }; -static const struct Entry DefaultEntry = { .color = Default }; - static uint gen; static struct Node *head; static struct Node *tail; -static struct Node *alloc(uint id, const char *key) { +static struct Node *alloc(uint id, const char *str, enum Color color) { struct Node *node = calloc(1, sizeof(*node)); if (!node) err(EX_OSERR, "calloc"); node->id = id; - node->key = strdup(key); - node->entry = DefaultEntry; - if (!node->key) err(EX_OSERR, "strdup"); + node->str = strdup(str); + if (!node->str) err(EX_OSERR, "strdup"); + node->color = color; + node->bits = 0; return node; } @@ -85,50 +84,68 @@ static struct Node *append(struct Node *node) { return node; } -static struct Node *find(uint id, const char *key) { +static struct Node *find(uint id, const char *str) { for (struct Node *node = head; node; node = node->next) { - if (node->id != id) continue; - if (strcmp(node->key, key)) continue; - return node; + if (node->id == id && !strcmp(node->str, str)) return node; } return NULL; } -static struct Node *insert(bool touch, uint id, const char *key) { - struct Node *node = find(id, key); - if (node && touch) { - return prepend(detach(node)); - } else if (node) { - return node; - } else if (touch) { - return prepend(alloc(id, key)); +void completePush(uint id, const char *str, enum Color color) { + struct Node *node = find(id, str); + if (node) { + if (color != Default) node->color = color; } else { - return append(alloc(id, key)); + append(alloc(id, str, color)); } } -struct Entry *cacheInsert(bool touch, uint id, const char *key) { - return &insert(touch, id, key)->entry; +void completePull(uint id, const char *str, enum Color color) { + struct Node *node = find(id, str); + if (node) { + if (color != Default) node->color = color; + prepend(detach(node)); + } else { + prepend(alloc(id, str, color)); + } } -const struct Entry *cacheGet(uint id, const char *key) { - struct Node *node = find(id, key); - return (node ? &node->entry : &DefaultEntry); +void completeReplace(const char *old, const char *new) { + struct Node *next = NULL; + for (struct Node *node = head; node; node = next) { + next = node->next; + if (strcmp(node->str, old)) continue; + free(node->str); + node->str = strdup(new); + if (!node->str) err(EX_OSERR, "strdup"); + prepend(detach(node)); + } } -void cacheReplace(bool touch, const char *old, const char *new) { +void completeRemove(uint id, const char *str) { struct Node *next = NULL; for (struct Node *node = head; node; node = next) { next = node->next; - if (strcmp(node->key, old)) continue; - free(node->key); - node->key = strdup(new); - if (!node->key) err(EX_OSERR, "strdup"); - if (touch) prepend(detach(node)); + if (id && node->id != id) continue; + if (str && strcmp(node->str, str)) continue; + detach(node); + free(node->str); + free(node); } + gen++; } -const char *cacheComplete(struct Cursor *curs, uint id, const char *prefix) { +enum Color completeColor(uint id, const char *str) { + struct Node *node = find(id, str); + return (node ? node->color : Default); +} + +uint *completeBits(uint id, const char *str) { + struct Node *node = find(id, str); + return (node ? &node->bits : NULL); +} + +const char *completePrefix(struct Cursor *curs, uint id, const char *prefix) { size_t len = strlen(prefix); if (curs->gen != gen) curs->node = NULL; for ( @@ -137,13 +154,12 @@ const char *cacheComplete(struct Cursor *curs, uint id, const char *prefix) { curs->node = curs->node->next ) { if (curs->node->id && curs->node->id != id) continue; - if (strncasecmp(curs->node->key, prefix, len)) continue; - return curs->node->key; + if (!strncasecmp(curs->node->str, prefix, len)) return curs->node->str; } return NULL; } -const char *cacheSearch(struct Cursor *curs, uint id, const char *substr) { +const char *completeSubstr(struct Cursor *curs, uint id, const char *substr) { if (curs->gen != gen) curs->node = NULL; for ( curs->gen = gen, curs->node = (curs->node ? curs->node->next : head); @@ -151,13 +167,24 @@ const char *cacheSearch(struct Cursor *curs, uint id, const char *substr) { curs->node = curs->node->next ) { if (curs->node->id && curs->node->id != id) continue; - if (!strstr(curs->node->key, substr)) continue; - return curs->node->key; + if (strstr(curs->node->str, substr)) return curs->node->str; + } + return NULL; +} + +const char *completeEach(struct Cursor *curs, uint id) { + if (curs->gen != gen) curs->node = NULL; + for ( + curs->gen = gen, curs->node = (curs->node ? curs->node->next : head); + curs->node; + curs->node = curs->node->next + ) { + if (curs->node->id == id) return curs->node->str; } return NULL; } -uint cacheID(struct Cursor *curs, const char *key) { +uint completeEachID(struct Cursor *curs, const char *str) { if (curs->gen != gen) curs->node = NULL; for ( curs->gen = gen, curs->node = (curs->node ? curs->node->next : head); @@ -165,45 +192,18 @@ uint cacheID(struct Cursor *curs, const char *key) { curs->node = curs->node->next ) { if (!curs->node->id) continue; - if (strcmp(curs->node->key, key)) continue; - return curs->node->id; + if (!strcmp(curs->node->str, str)) return curs->node->id; } return None; } -void cacheAccept(struct Cursor *curs) { +void completeAccept(struct Cursor *curs) { if (curs->gen == gen && curs->node) { prepend(detach(curs->node)); } curs->node = NULL; } -void cacheReject(struct Cursor *curs) { +void completeReject(struct Cursor *curs) { curs->node = NULL; } - -void cacheRemove(uint id, const char *key) { - gen++; - struct Node *next = NULL; - for (struct Node *node = head; node; node = next) { - next = node->next; - if (id && node->id != id) continue; - if (strcmp(node->key, key)) continue; - detach(node); - free(node->key); - free(node); - if (id) break; - } -} - -void cacheClear(uint id) { - gen++; - struct Node *next = NULL; - for (struct Node *node = head; node; node = next) { - next = node->next; - if (node->id != id) continue; - detach(node); - free(node->key); - free(node); - } -} diff --git a/configure b/configure index 9465b77..07e3245 100755 --- a/configure +++ b/configure @@ -29,6 +29,7 @@ for opt; do (--prefix=*) echo "PREFIX = ${opt#*=}" ;; (--bindir=*) echo "BINDIR = ${opt#*=}" ;; (--mandir=*) echo "MANDIR = ${opt#*=}" ;; + (--enable-sandman) echo 'BINS += sandman' ;; (*) echo "warning: unsupported option ${opt}" >&2 ;; esac done diff --git a/handle.c b/handle.c index b8434c6..5a2cf7c 100644 --- a/handle.c +++ b/handle.c @@ -266,7 +266,7 @@ static void handleErrorSASLFail(struct Message *msg) { static void handleReplyWelcome(struct Message *msg) { require(msg, false, 1); set(&self.nick, msg->params[0]); - cacheInsert(true, Network, self.nick); + completePull(Network, self.nick, Default); if (self.mode) ircFormat("MODE %s %s\r\n", self.nick, self.mode); if (self.join) { uint count = 1; @@ -278,7 +278,7 @@ static void handleReplyWelcome(struct Message *msg) { replies[ReplyTopicAuto] += count; replies[ReplyNamesAuto] += count; } - commandCache(); + commandCompletion(); handleReplyGeneric(msg); } @@ -372,13 +372,13 @@ static void handleJoin(struct Message *msg) { set(&self.host, msg->host); } idColors[id] = hash(msg->params[0]); - cacheInsert(true, None, msg->params[0])->color = idColors[id]; + completePull(None, msg->params[0], idColors[id]); if (replies[ReplyJoin]) { windowShow(windowFor(id)); replies[ReplyJoin]--; } } - cacheInsert(true, id, msg->nick)->color = hash(msg->user); + completePull(id, msg->nick, hash(msg->user)); if (msg->params[2] && !strcasecmp(msg->params[2], msg->nick)) { msg->params[2] = NULL; } @@ -410,9 +410,9 @@ static void handlePart(struct Message *msg) { require(msg, true, 1); uint id = idFor(msg->params[0]); if (!strcmp(msg->nick, self.nick)) { - cacheClear(id); + completeRemove(id, NULL); } - cacheRemove(id, msg->nick); + completeRemove(id, msg->nick); enum Heat heat = filterCheck(Cold, id, msg); if (heat > Ice) urlScan(id, msg->nick, msg->params[1]); uiFormat( @@ -432,14 +432,14 @@ static void handleKick(struct Message *msg) { require(msg, true, 2); uint id = idFor(msg->params[0]); bool kicked = !strcmp(msg->params[1], self.nick); - cacheInsert(true, id, msg->nick)->color = hash(msg->user); + completePull(id, msg->nick, hash(msg->user)); urlScan(id, msg->nick, msg->params[2]); uiFormat( id, (kicked ? Hot : Cold), tagTime(msg), "%s\3%02d%s\17\tkicks \3%02d%s\3 out of \3%02d%s\3%s%s", (kicked ? "\26" : ""), hash(msg->user), msg->nick, - cacheGet(id, msg->params[1])->color, msg->params[1], + completeColor(id, msg->params[1]), msg->params[1], hash(msg->params[0]), msg->params[0], (msg->params[2] ? ": " : ""), (msg->params[2] ?: "") ); @@ -448,8 +448,8 @@ static void handleKick(struct Message *msg) { msg->nick, msg->params[1], msg->params[0], (msg->params[2] ? ": " : ""), (msg->params[2] ?: "") ); - cacheRemove(id, msg->params[1]); - if (kicked) cacheClear(id); + completeRemove(id, msg->params[1]); + if (kicked) completeRemove(id, NULL); } static void handleNick(struct Message *msg) { @@ -459,7 +459,7 @@ static void handleNick(struct Message *msg) { inputUpdate(); } struct Cursor curs = {0}; - for (uint id; (id = cacheID(&curs, msg->nick));) { + for (uint id; (id = completeEachID(&curs, msg->nick));) { if (!strcmp(idNames[id], msg->nick)) { set(&idNames[id], msg->params[0]); } @@ -474,13 +474,13 @@ static void handleNick(struct Message *msg) { msg->nick, msg->params[0] ); } - cacheReplace(true, msg->nick, msg->params[0]); + completeReplace(msg->nick, msg->params[0]); } static void handleSetname(struct Message *msg) { require(msg, true, 1); struct Cursor curs = {0}; - for (uint id; (id = cacheID(&curs, msg->nick));) { + for (uint id; (id = completeEachID(&curs, msg->nick));) { uiFormat( id, filterCheck(Cold, id, msg), tagTime(msg), "\3%02d%s\3\tis now known as \3%02d%s\3 (%s\17)", @@ -493,7 +493,7 @@ static void handleSetname(struct Message *msg) { static void handleQuit(struct Message *msg) { require(msg, true, 0); struct Cursor curs = {0}; - for (uint id; (id = cacheID(&curs, msg->nick));) { + for (uint id; (id = completeEachID(&curs, msg->nick));) { enum Heat heat = filterCheck(Cold, id, msg); if (heat > Ice) urlScan(id, msg->nick, msg->params[0]); uiFormat( @@ -509,7 +509,7 @@ static void handleQuit(struct Message *msg) { (msg->params[0] ? ": " : ""), (msg->params[0] ?: "") ); } - cacheRemove(None, msg->nick); + completeRemove(None, msg->nick); } static void handleInvite(struct Message *msg) { @@ -555,17 +555,11 @@ static void handleErrorUserOnChannel(struct Message *msg) { uiFormat( id, Warm, tagTime(msg), "\3%02d%s\3 is already in \3%02d%s\3", - cacheGet(id, msg->params[1])->color, msg->params[1], + completeColor(id, msg->params[1]), msg->params[1], hash(msg->params[2]), msg->params[2] ); } -static uint prefixBit(char p) { - char *s = strchr(network.prefixes, p); - if (!s) return 0; - return 1 << (s - network.prefixes); -} - static void handleReplyNames(struct Message *msg) { require(msg, false, 4); uint id = idFor(msg->params[2]); @@ -581,9 +575,8 @@ static void handleReplyNames(struct Message *msg) { for (char *p = prefixes; p < nick; ++p) { bits |= prefixBit(*p); } - struct Entry *entry = cacheInsert(false, id, nick); - if (user) entry->color = color; - entry->prefixBits = bits; + completePush(id, nick, color); + *completeBits(id, nick) = bits; if (!replies[ReplyNames] && !replies[ReplyNamesAuto]) continue; ptr = seprintf( ptr, end, "%s\3%02d%s\3", (ptr > buf ? ", " : ""), color, prefixes @@ -606,41 +599,6 @@ static void handleReplyEndOfNames(struct Message *msg) { } } -static struct { - char buf[1024]; - char *ptr; - char *end; -} who = { - .ptr = who.buf, - .end = &who.buf[sizeof(who.buf)], -}; - -static void handleReplyWho(struct Message *msg) { - require(msg, false, 7); - if (who.ptr == who.buf) { - who.ptr = seprintf( - who.ptr, who.end, "The council of \3%02d%s\3 are ", - hash(msg->params[1]), msg->params[1] - ); - } - char *prefixes = &msg->params[6][1]; - if (prefixes[0] == '*') prefixes++; - prefixes[strspn(prefixes, network.prefixes)] = '\0'; - if (!prefixes[0] || prefixes[0] == '+') return; - who.ptr = seprintf( - who.ptr, who.end, "%s\3%02d%s%s\3%s", - (who.ptr[-1] == ' ' ? "" : ", "), - hash(msg->params[2]), prefixes, msg->params[5], - (msg->params[6][0] == 'H' ? "" : " (away)") - ); -} - -static void handleReplyEndOfWho(struct Message *msg) { - require(msg, false, 2); - uiWrite(idFor(msg->params[1]), Warm, tagTime(msg), who.buf); - who.ptr = who.buf; -} - static void handleReplyNoTopic(struct Message *msg) { require(msg, false, 2); uiFormat( @@ -650,24 +608,24 @@ static void handleReplyNoTopic(struct Message *msg) { ); } -static void topicCache(uint id, const char *topic) { +static void topicComplete(uint id, const char *topic) { char buf[512]; struct Cursor curs = {0}; - const char *prev = cacheComplete(&curs, id, "/topic "); + const char *prev = completePrefix(&curs, id, "/topic "); if (prev) { snprintf(buf, sizeof(buf), "%s", prev); - cacheRemove(id, buf); + completeRemove(id, buf); } if (topic) { snprintf(buf, sizeof(buf), "/topic %s", topic); - cacheInsert(false, id, buf); + completePush(id, buf, Default); } } static void handleReplyTopic(struct Message *msg) { require(msg, false, 3); uint id = idFor(msg->params[1]); - topicCache(id, msg->params[2]); + topicComplete(id, msg->params[2]); if (!replies[ReplyTopic] && !replies[ReplyTopicAuto]) return; urlScan(id, NULL, msg->params[2]); uiFormat( @@ -718,7 +676,7 @@ static void handleTopic(struct Message *msg) { require(msg, true, 2); uint id = idFor(msg->params[0]); if (!msg->params[1][0]) { - topicCache(id, NULL); + topicComplete(id, NULL); uiFormat( id, Warm, tagTime(msg), "\3%02d%s\3\tremoves the sign in \3%02d%s\3", @@ -732,7 +690,7 @@ static void handleTopic(struct Message *msg) { } struct Cursor curs = {0}; - const char *prev = cacheComplete(&curs, id, "/topic "); + const char *prev = completePrefix(&curs, id, "/topic "); if (prev) { prev += 7; } else { @@ -782,7 +740,7 @@ log: id, tagTime(msg), "%s places a new sign in %s: %s", msg->nick, msg->params[0], msg->params[1] ); - topicCache(id, msg->params[1]); + topicComplete(id, msg->params[1]); urlScan(id, msg->nick, msg->params[1]); } @@ -905,16 +863,17 @@ static void handleMode(struct Message *msg) { char prefix = network.prefixes[ strchr(network.prefixModes, *ch) - network.prefixModes ]; + completePush(id, nick, Default); if (set) { - cacheInsert(false, id, nick)->prefixBits |= prefixBit(prefix); + *completeBits(id, nick) |= prefixBit(prefix); } else { - cacheInsert(false, id, nick)->prefixBits &= ~prefixBit(prefix); + *completeBits(id, nick) &= ~prefixBit(prefix); } uiFormat( id, Cold, tagTime(msg), "\3%02d%s\3\t%s \3%02d%c%s\3 %s%s in \3%02d%s\3", hash(msg->user), msg->nick, verb, - cacheGet(id, nick)->color, prefix, nick, + completeColor(id, nick), prefix, nick, mode, name, hash(msg->params[0]), msg->params[0] ); logFormat( @@ -1052,7 +1011,7 @@ static void handleReplyBanList(struct Message *msg) { id, Warm, tagTime(msg), "Banned from \3%02d%s\3 since %s by \3%02d%s\3: %s", hash(msg->params[1]), msg->params[1], - since, cacheGet(id, msg->params[3])->color, msg->params[3], + since, completeColor(id, msg->params[3]), msg->params[3], msg->params[2] ); } else { @@ -1075,7 +1034,7 @@ static void onList(const char *list, struct Message *msg) { id, Warm, tagTime(msg), "On the \3%02d%s\3 %s list since %s by \3%02d%s\3: %s", hash(msg->params[1]), msg->params[1], list, - since, cacheGet(id, msg->params[3])->color, msg->params[3], + since, completeColor(id, msg->params[3]), msg->params[3], msg->params[2] ); } else { @@ -1096,19 +1055,19 @@ static void handleReplyInviteList(struct Message *msg) { } static void handleReplyList(struct Message *msg) { - require(msg, false, 4); + require(msg, false, 3); uiFormat( Network, Warm, tagTime(msg), "In \3%02d%s\3 are %ld under the banner: %s", hash(msg->params[1]), msg->params[1], strtol(msg->params[2], NULL, 10), - msg->params[3] + (msg->params[3] ?: "") ); } static void handleReplyWhoisUser(struct Message *msg) { require(msg, false, 6); - cacheInsert(true, Network, msg->params[1])->color = hash(msg->params[2]); + completePull(Network, msg->params[1], hash(msg->params[2])); uiFormat( Network, Warm, tagTime(msg), "\3%02d%s\3\tis %s!%s@%s (%s\17)", @@ -1123,7 +1082,7 @@ static void handleReplyWhoisServer(struct Message *msg) { uiFormat( Network, Warm, tagTime(msg), "\3%02d%s\3\t%s connected to %s (%s)", - cacheGet(Network, msg->params[1])->color, msg->params[1], + completeColor(Network, msg->params[1]), msg->params[1], (replies[ReplyWhowas] ? "was" : "is"), msg->params[2], msg->params[3] ); } @@ -1147,7 +1106,7 @@ static void handleReplyWhoisIdle(struct Message *msg) { uiFormat( Network, Warm, tagTime(msg), "\3%02d%s\3\tis idle for %lu %s%s%s%s", - cacheGet(Network, msg->params[1])->color, msg->params[1], + completeColor(Network, msg->params[1]), msg->params[1], idle, unit, (idle != 1 ? "s" : ""), (msg->params[3] ? ", signed on " : ""), (msg->params[3] ? signon : "") ); @@ -1169,7 +1128,7 @@ static void handleReplyWhoisChannels(struct Message *msg) { uiFormat( Network, Warm, tagTime(msg), "\3%02d%s\3\tis in %s", - cacheGet(Network, msg->params[1])->color, msg->params[1], buf + completeColor(Network, msg->params[1]), msg->params[1], buf ); } @@ -1183,7 +1142,7 @@ static void handleReplyWhoisGeneric(struct Message *msg) { uiFormat( Network, Warm, tagTime(msg), "\3%02d%s\3\t%s%s%s", - cacheGet(Network, msg->params[1])->color, msg->params[1], + completeColor(Network, msg->params[1]), msg->params[1], msg->params[2], (msg->params[3] ? " " : ""), (msg->params[3] ?: "") ); } @@ -1191,13 +1150,13 @@ static void handleReplyWhoisGeneric(struct Message *msg) { static void handleReplyEndOfWhois(struct Message *msg) { require(msg, false, 2); if (strcmp(msg->params[1], self.nick)) { - cacheRemove(Network, msg->params[1]); + completeRemove(Network, msg->params[1]); } } static void handleReplyWhowasUser(struct Message *msg) { require(msg, false, 6); - cacheInsert(true, Network, msg->params[1])->color = hash(msg->params[2]); + completePull(Network, msg->params[1], hash(msg->params[2])); uiFormat( Network, Warm, tagTime(msg), "\3%02d%s\3\twas %s!%s@%s (%s)", @@ -1209,7 +1168,7 @@ static void handleReplyWhowasUser(struct Message *msg) { static void handleReplyEndOfWhowas(struct Message *msg) { require(msg, false, 2); if (strcmp(msg->params[1], self.nick)) { - cacheRemove(Network, msg->params[1]); + completeRemove(Network, msg->params[1]); } } @@ -1220,7 +1179,7 @@ static void handleReplyAway(struct Message *msg) { uiFormat( id, (id == Network ? Warm : Cold), tagTime(msg), "\3%02d%s\3\tis away: %s", - cacheGet(id, msg->params[1])->color, msg->params[1], msg->params[2] + completeColor(id, msg->params[1]), msg->params[1], msg->params[2] ); logFormat( id, tagTime(msg), "%s is away: %s", @@ -1291,7 +1250,7 @@ static char *colorMentions(char *ptr, char *end, uint id, const char *msg) { size_t len = strcspn(msg, ",:<> "); char *p = seprintf(ptr, end, "%.*s", (int)len, msg); - enum Color color = cacheGet(id, ptr)->color; + enum Color color = completeColor(id, ptr); if (color != Default) { ptr = seprintf(ptr, end, "\3%02d%.*s\3", color, (int)len, msg); } else { @@ -1331,7 +1290,7 @@ static void handlePrivmsg(struct Message *msg) { heat = filterCheck(heat, id, msg); if (heat > Warm && !mine && !query) highlight = true; if (!notice && !mine && heat > Ice) { - cacheInsert(true, id, msg->nick)->color = hash(msg->user); + completePull(id, msg->nick, hash(msg->user)); } if (heat > Ice) urlScan(id, msg->nick, msg->params[1]); @@ -1398,7 +1357,6 @@ static const struct Handler { { "312", 0, handleReplyWhoisServer }, { "313", +ReplyWhois, handleReplyWhoisGeneric }, { "314", +ReplyWhowas, handleReplyWhowasUser }, - { "315", -ReplyWho, handleReplyEndOfWho }, { "317", +ReplyWhois, handleReplyWhoisIdle }, { "318", -ReplyWhois, handleReplyEndOfWhois }, { "319", +ReplyWhois, handleReplyWhoisChannels }, @@ -1416,7 +1374,6 @@ static const struct Handler { { "347", -ReplyInvex, NULL }, { "348", +ReplyExcepts, handleReplyExceptList }, { "349", -ReplyExcepts, NULL }, - { "352", +ReplyWho, handleReplyWho }, { "353", 0, handleReplyNames }, { "366", 0, handleReplyEndOfNames }, { "367", +ReplyBan, handleReplyBanList }, diff --git a/input.c b/input.c index bcefee5..6b33b93 100644 --- a/input.c +++ b/input.c @@ -261,12 +261,12 @@ static const struct { { L"\\wave", L"ヾ(^∇^)" }, }; -void inputCache(void) { +void inputCompletion(void) { char mbs[256]; for (size_t i = 0; i < ARRAY_LEN(Macros); ++i) { size_t n = wcstombs(mbs, Macros[i].name, sizeof(mbs)); assert(n != (size_t)-1); - cacheInsert(false, None, mbs); + completePush(None, mbs, Default); } } @@ -300,12 +300,12 @@ static struct { } tab; static void tabAccept(void) { - cacheAccept(&tab.curs); + completeAccept(&tab.curs); tab.len = 0; } static void tabReject(void) { - cacheReject(&tab.curs); + completeReject(&tab.curs); tab.len = 0; } @@ -333,9 +333,9 @@ static int tabComplete(struct Edit *e, uint id) { tab.suffix = true; } - const char *comp = cacheComplete(&tab.curs, id, tab.pre); + const char *comp = completePrefix(&tab.curs, id, tab.pre); if (!comp) { - comp = cacheComplete(&tab.curs, id, tab.pre); + comp = completePrefix(&tab.curs, id, tab.pre); tab.suffix ^= true; } if (!comp) { diff --git a/scripts/sandman.1 b/sandman.1 index 92828c0..92828c0 100644 --- a/scripts/sandman.1 +++ b/sandman.1 diff --git a/scripts/sandman.m b/sandman.m index 2e5c4db..2e5c4db 100644 --- a/scripts/sandman.m +++ b/sandman.m diff --git a/scripts/.gitignore b/scripts/.gitignore deleted file mode 100644 index f6dc107..0000000 --- a/scripts/.gitignore +++ /dev/null @@ -1 +0,0 @@ -sandman diff --git a/scripts/Makefile b/scripts/Makefile deleted file mode 100644 index 179a2d3..0000000 --- a/scripts/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -PREFIX ?= /usr/local -BINDIR ?= ${PREFIX}/bin -MANDIR ?= ${PREFIX}/man - -CFLAGS += -Wall -Wextra - --include ../config.mk - -LDLIBS = -framework Cocoa - -all: sandman - -clean: - rm -f sandman - -install: sandman sandman.1 - install -d ${DESTDIR}${BINDIR} ${DESTDIR}${MANDIR}/man1 - install sandman ${DESTDIR}${BINDIR} - install -m 644 sandman.1 ${DESTDIR}${MANDIR}/man1 - -uninstall: - rm -f ${DESTDIR}${BINDIR}/sandman ${DESTDIR}/man/man1/sandman.1 diff --git a/ui.c b/ui.c index b2c3b4b..079ee19 100644 --- a/ui.c +++ b/ui.c @@ -157,7 +157,7 @@ void uiDraw(void) { if (!to_status_line) return; if (!strcmp(uiTitle, prevTitle)) return; strcpy(prevTitle, uiTitle); - putp(tiparm(to_status_line, 0)); + putp(tparm(to_status_line, 0)); putp(uiTitle); putp(from_status_line); fflush(stdout); @@ -296,7 +296,7 @@ static size_t signatureVersion(uint64_t signature) { for (size_t i = 0; i < ARRAY_LEN(Signatures); ++i) { if (signature == Signatures[i]) return i; } - errx(EX_DATAERR, "unknown file signature %" PRIX64, signature); + errx(EX_DATAERR, "unknown save file signature %" PRIX64, signature); } static int writeUint64(FILE *file, uint64_t u) { @@ -318,7 +318,7 @@ static uint64_t readUint64(FILE *file) { uint64_t u; fread(&u, sizeof(u), 1, file); if (ferror(file)) err(EX_IOERR, "fread"); - if (feof(file)) errx(EX_DATAERR, "unexpected eof"); + if (feof(file)) errx(EX_DATAERR, "unexpected end of save file"); return u; } diff --git a/window.c b/window.c index 0c675e9..f700fd7 100644 --- a/window.c +++ b/window.c @@ -93,7 +93,7 @@ static struct Window *windowRemove(uint num) { } static void windowFree(struct Window *window) { - cacheRemove(None, idNames[window->id]); + completeRemove(None, idNames[window->id]); bufferFree(window->buffer); free(window); } @@ -118,7 +118,7 @@ uint windowFor(uint id) { window->thresh = windowThreshold; } window->buffer = bufferAlloc(); - cacheInsert(false, None, idNames[id])->color = idColors[id]; + completePush(None, idNames[id], idColors[id]); return windowPush(window); } @@ -147,6 +147,7 @@ static int styleAdd(WINDOW *win, struct Style init, const char *str) { struct Style style = init; while (*str) { size_t len = styleParse(&style, &str); + if (!len) continue; wattr_set(win, uiAttr(style), uiPair(style), NULL); if (waddnstr(win, str, len) == ERR) return -1; @@ -477,7 +478,7 @@ void windowClose(uint num) { if (num >= count) return; if (windows[num]->id == Network) return; struct Window *window = windowRemove(num); - cacheClear(window->id); + completeRemove(window->id, NULL); windowFree(window); if (swap >= num) swap--; if (show == num) { @@ -622,7 +623,7 @@ static time_t readTime(FILE *file) { time_t time; fread(&time, sizeof(time), 1, file); if (ferror(file)) err(EX_IOERR, "fread"); - if (feof(file)) errx(EX_DATAERR, "unexpected eof"); + if (feof(file)) errx(EX_DATAERR, "unexpected end of save file"); return time; } |