diff --git a/chat.c b/chat.c index 16ecb8a..fa1240a 100644 --- a/chat.c +++ b/chat.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,7 @@ static void genCert(const char *path) { const char *name = strrchr(path, '/'); name = (name ? &name[1] : path); - char subj[256]; + char subj[4 + NAME_MAX]; snprintf(subj, sizeof(subj), "/CN=%.*s", (int)strcspn(name, "."), name); umask(0066); execlp( @@ -56,13 +57,11 @@ char *idNames[IDCap] = { [Debug] = "", [Network] = "", }; - enum Color idColors[IDCap] = { [None] = Black, [Debug] = Green, [Network] = Gray, }; - uint idNext = Network + 1; struct Network network; @@ -79,21 +78,9 @@ static void exitSave(void) { uint32_t hashInit; -int utilPipe[2] = { -1, -1 }; +uint execID; int execPipe[2] = { -1, -1 }; - -static void utilRead(void) { - char buf[1024]; - ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1); - if (len < 0) err(EX_IOERR, "read"); - if (!len) return; - buf[len] = '\0'; - char *ptr = buf; - while (ptr) { - char *line = strsep(&ptr, "\n"); - if (line[0]) uiFormat(Network, Warm, NULL, "%s", line); - } -} +int utilPipe[2] = { -1, -1 }; static void execRead(void) { char buf[1024]; @@ -108,6 +95,19 @@ static void execRead(void) { } } +static void utilRead(void) { + char buf[1024]; + ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1); + if (len < 0) err(EX_IOERR, "read"); + if (!len) return; + buf[len] = '\0'; + char *ptr = buf; + while (ptr) { + char *line = strsep(&ptr, "\n"); + if (line[0]) uiFormat(Network, Warm, NULL, "%s", line); + } +} + static volatile sig_atomic_t signals[NSIG]; static void signalHandler(int signal) { signals[signal] = 1; @@ -188,7 +188,7 @@ int main(int argc, char *argv[]) { if (!user) user = nick; if (!real) real = nick; - set(&network.name, host); + // Modes defined in RFC 1459: set(&network.chanTypes, "#&"); set(&network.prefixes, "@+"); set(&network.prefixModes, "ov"); @@ -196,6 +196,8 @@ int main(int argc, char *argv[]) { set(&network.paramModes, "k"); set(&network.setParamModes, "l"); set(&network.channelModes, "imnpst"); + + set(&network.name, host); set(&self.nick, "*"); commandComplete(); diff --git a/chat.h b/chat.h index 1f4274f..d7f7c5c 100644 --- a/chat.h +++ b/chat.h @@ -65,8 +65,8 @@ static inline uint idFor(const char *name) { if (id) return id; if (idNext == IDCap) return Network; idNames[idNext] = strdup(name); - if (!idNames[idNext]) err(EX_OSERR, "strdup"); idColors[idNext] = Default; + if (!idNames[idNext]) err(EX_OSERR, "strdup"); return idNext++; } @@ -79,9 +79,22 @@ static inline enum Color hash(const char *str) { hash ^= *str; hash *= 0x27220A95; } - return 2 + hash % 74; + return Blue + hash % 74; } +extern struct Network { + char *name; + char *chanTypes; + char *prefixes; + char *prefixModes; + char *listModes; + char *paramModes; + char *setParamModes; + char *channelModes; + char excepts; + char invex; +} network; + #define ENUM_CAP \ X("extended-join", CapExtendedJoin) \ X("invite-notify", CapInviteNotify) \ @@ -96,25 +109,12 @@ enum Cap { #undef X }; -extern struct Network { - char *name; - char *chanTypes; - char *prefixes; - char *prefixModes; - char *listModes; - char *paramModes; - char *setParamModes; - char *channelModes; - char excepts; - char invex; -} network; - extern struct Self { bool debug; bool restricted; - char *plain; - const char *join; enum Cap caps; + char *plain; + char *join; char *nick; char *user; enum Color color; @@ -155,25 +155,8 @@ void ircFormat(const char *format, ...) __attribute__((format(printf, 1, 2))); void ircClose(void); -extern struct Replies { - uint away; - uint join; - uint list; - uint names; - uint topic; - uint whois; -} replies; - uint execID; int execPipe[2]; - -void handle(struct Message msg); -void command(uint id, char *input); -const char *commandIsPrivmsg(uint id, const char *input); -const char *commandIsNotice(uint id, const char *input); -const char *commandIsAction(uint id, const char *input); -void commandComplete(void); - int utilPipe[2]; enum { UtilCap = 16 }; @@ -190,6 +173,22 @@ static inline void utilPush(struct Util *util, const char *arg) { } } +extern struct Replies { + uint away; + uint join; + uint list; + uint names; + uint topic; + uint whois; +} replies; + +void handle(struct Message msg); +void command(uint id, char *input); +const char *commandIsPrivmsg(uint id, const char *input); +const char *commandIsNotice(uint id, const char *input); +const char *commandIsAction(uint id, const char *input); +void commandComplete(void); + enum Heat { Cold, Warm, Hot }; extern struct Util uiNotifyUtil; void uiInit(void); diff --git a/command.c b/command.c index 58d5a66..cc650da 100644 --- a/command.c +++ b/command.c @@ -39,53 +39,45 @@ static void commandQuote(uint id, char *params) { if (params) ircFormat("%s\r\n", params); } -static void commandPrivmsg(uint id, char *params) { +static void echoMessage(char *cmd, uint id, char *params) { if (!params || !params[0]) return; - ircFormat("PRIVMSG %s :%s\r\n", idNames[id], params); + ircFormat("%s %s :%s\r\n", cmd, idNames[id], params); struct Message msg = { .nick = self.nick, .user = self.user, - .cmd = "PRIVMSG", + .cmd = cmd, .params[0] = idNames[id], .params[1] = params, }; handle(msg); } +static void commandPrivmsg(uint id, char *params) { + echoMessage("PRIVMSG", id, params); +} + static void commandNotice(uint id, char *params) { - if (!params || !params[0]) return; - ircFormat("NOTICE %s :%s\r\n", idNames[id], params); - struct Message msg = { - .nick = self.nick, - .user = self.user, - .cmd = "NOTICE", - .params[0] = idNames[id], - .params[1] = params, - }; - handle(msg); + echoMessage("NOTICE", id, params); } static void commandMe(uint id, char *params) { char buf[512]; snprintf(buf, sizeof(buf), "\1ACTION %s\1", (params ? params : "")); - commandPrivmsg(id, buf); + echoMessage("PRIVMSG", id, buf); } static void commandMsg(uint id, char *params) { - (void)id; - char *nick = strsep(¶ms, " "); - if (!params) return; - commandPrivmsg(idFor(nick), params); + id = idFor(strsep(¶ms, " ")); + echoMessage("PRIVMSG", id, params); } static void commandJoin(uint id, char *params) { + if (!params) params = idNames[id]; uint count = 1; - if (params) { - for (char *ch = params; *ch && *ch != ' '; ++ch) { - if (*ch == ',') count++; - } + for (char *ch = params; *ch && *ch != ' '; ++ch) { + if (*ch == ',') count++; } - ircFormat("JOIN %s\r\n", (params ? params : idNames[id])); + ircFormat("JOIN %s\r\n", params); replies.join += count; replies.topic += count; replies.names += count; @@ -101,7 +93,7 @@ static void commandPart(uint id, char *params) { static void commandQuit(uint id, char *params) { (void)id; - set(&self.quit, (params ? params : "Goodbye")); + set(&self.quit, (params ? params : "nyaa~")); } static void commandNick(uint id, char *params) { @@ -131,7 +123,7 @@ static void commandTopic(uint id, char *params) { static void commandNames(uint id, char *params) { (void)params; - ircFormat("NAMES :%s\r\n", idNames[id]); + ircFormat("NAMES %s\r\n", idNames[id]); replies.names++; } @@ -170,14 +162,12 @@ static void commandWhois(uint id, char *params) { static void commandNS(uint id, char *params) { (void)id; - if (!params) return; - ircFormat("PRIVMSG NickServ :%s\r\n", params); + if (params) ircFormat("PRIVMSG NickServ :%s\r\n", params); } static void commandCS(uint id, char *params) { (void)id; - if (!params) return; - ircFormat("PRIVMSG ChanServ :%s\r\n", params); + if (params) ircFormat("PRIVMSG ChanServ :%s\r\n", params); } static void commandQuery(uint id, char *params) { @@ -330,36 +320,41 @@ const char *commandIsAction(uint id, const char *input) { void command(uint id, char *input) { if (id == Debug && input[0] != '/') { commandQuote(id, input); + return; } else if (commandIsPrivmsg(id, input)) { commandPrivmsg(id, input); + return; } else if (input[0] == '/' && isdigit(input[1])) { commandWindow(id, &input[1]); - } else { - const char *cmd = strsep(&input, " "); - const char *unique = complete(None, cmd); - if (unique && !complete(None, cmd)) { - cmd = unique; - completeReject(); - } - const struct Handler *handler = bsearch( - cmd, Commands, ARRAY_LEN(Commands), sizeof(*handler), compar - ); - if (self.restricted && handler && handler->restricted) { - handler = NULL; - } - if (handler) { - if (input) { - input += strspn(input, " "); - size_t len = strlen(input); - while (input[len - 1] == ' ') input[--len] = '\0'; - if (!input[0]) input = NULL; - } - if (input && !input[0]) input = NULL; - handler->fn(id, input); - } else { - uiFormat(id, Hot, NULL, "No such command %s", cmd); - } + return; } + + const char *cmd = strsep(&input, " "); + const char *unique = complete(None, cmd); + if (unique && !complete(None, cmd)) { + cmd = unique; + completeReject(); + } + + const struct Handler *handler = bsearch( + cmd, Commands, ARRAY_LEN(Commands), sizeof(*handler), compar + ); + if (!handler) { + uiFormat(id, Warm, NULL, "No such command %s", cmd); + return; + } + if (self.restricted && handler->restricted) { + uiFormat(id, Warm, NULL, "Command %s is restricted", cmd); + return; + } + + if (input) { + input += strspn(input, " "); + size_t len = strlen(input); + while (input[len - 1] == ' ') input[--len] = '\0'; + if (!input[0]) input = NULL; + } + handler->fn(id, input); } void commandComplete(void) { diff --git a/complete.c b/complete.c index b65d870..ad9bfd7 100644 --- a/complete.c +++ b/complete.c @@ -35,10 +35,10 @@ static struct Node *alloc(uint id, const char *str, enum Color color) { if (!node) err(EX_OSERR, "malloc"); node->id = id; node->str = strdup(str); - if (!node->str) err(EX_OSERR, "strdup"); node->color = color; node->prev = NULL; node->next = NULL; + if (!node->str) err(EX_OSERR, "strdup"); return node; } @@ -75,7 +75,9 @@ static struct Node *append(struct Node *node) { static struct Node *find(uint id, const char *str) { for (struct Node *node = head; node; node = node->next) { - if (node->id == id && !strcmp(node->str, str)) return node; + if (node->id != id) continue; + if (strcmp(node->str, str)) continue; + return node; } return NULL; } @@ -86,7 +88,7 @@ void completeAdd(uint id, const char *str, enum Color color) { void completeTouch(uint id, const char *str, enum Color color) { struct Node *node = find(id, str); - if (node && node->color != color) node->color = color; + if (node) node->color = color; prepend(node ? detach(node) : alloc(id, str, color)); } @@ -128,11 +130,10 @@ void completeReplace(uint id, const char *old, const char *new) { next = node->next; if (id && node->id != id) continue; if (strcmp(node->str, old)) continue; - if (match == node) match = NULL; free(node->str); node->str = strdup(new); - if (!node->str) err(EX_OSERR, "strdup"); prepend(detach(node)); + if (!node->str) err(EX_OSERR, "strdup"); } } diff --git a/irc.c b/irc.c index 704caa6..6a65c79 100644 --- a/irc.c +++ b/irc.c @@ -157,15 +157,17 @@ int ircConnect(const char *bindHost, const char *host, const char *port) { return sock; } -static void debug(char dir, const char *line) { +enum { MessageCap = 8191 + 512 }; + +static void debug(const char *pre, const char *line) { if (!self.debug) return; size_t len = strcspn(line, "\r\n"); uiFormat( - Debug, Cold, NULL, "\3%d%c%c\3\t%.*s", - Gray, dir, dir, (int)len, line + Debug, Cold, NULL, "\3%02d%s\3\t%.*s", + Gray, pre, (int)len, line ); if (!isatty(STDERR_FILENO)) { - fprintf(stderr, "%c%c %.*s\n", dir, dir, (int)len, line); + fprintf(stderr, "%s %.*s\n", pre, (int)len, line); } } @@ -181,13 +183,13 @@ void ircSend(const char *ptr, size_t len) { } void ircFormat(const char *format, ...) { - char buf[1024]; + char buf[MessageCap]; va_list ap; va_start(ap, format); int len = vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); assert((size_t)len < sizeof(buf)); - debug('<', buf); + debug("<<", buf); ircSend(buf, len); } @@ -249,7 +251,7 @@ static struct Message parse(char *line) { } void ircRecv(void) { - static char buf[8191 + 512]; + static char buf[MessageCap]; static size_t len = 0; assert(client); @@ -265,7 +267,7 @@ void ircRecv(void) { crlf = memmem(line, &buf[len] - line, "\r\n", 2); if (!crlf) break; *crlf = '\0'; - debug('>', line); + debug(">>", line); handle(parse(line)); line = crlf + 2; } diff --git a/ui.c b/ui.c index aaa7b49..bcb1003 100644 --- a/ui.c +++ b/ui.c @@ -90,10 +90,10 @@ struct Window { }; static struct { - uint show; - uint swap; struct Window *ptrs[IDCap]; uint len; + uint show; + uint swap; } windows; static uint windowPush(struct Window *window) { diff --git a/url.c b/url.c index 64fdd8b..949f860 100644 --- a/url.c +++ b/url.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -142,8 +143,10 @@ static void urlCopy(const char *url) { int error = pipe(rw); if (error) err(EX_OSERR, "pipe"); - ssize_t len = write(rw[1], url, strlen(url)); - if (len < 0) err(EX_IOERR, "write"); + size_t len = strlen(url); + if (len > PIPE_BUF) len = PIPE_BUF; + ssize_t n = write(rw[1], url, len); + if (n < 0) err(EX_IOERR, "write"); error = close(rw[1]); if (error) err(EX_IOERR, "close");