about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--chat.c24
-rw-r--r--chat.h67
-rw-r--r--command.c103
-rw-r--r--complete.c11
-rw-r--r--irc.c18
-rw-r--r--ui.c4
-rw-r--r--url.c7
7 files changed, 118 insertions, 116 deletions
diff --git a/chat.c b/chat.c
index 16ecb8a..fa1240a 100644
--- a/chat.c
+++ b/chat.c
@@ -17,6 +17,7 @@
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <locale.h>
 #include <poll.h>
 #include <signal.h>
@@ -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] = "<debug>",
 	[Network] = "<network>",
 };
-
 enum Color idColors[IDCap] = {
 	[None] = Black,
 	[Debug] = Green,
 	[Network] = Gray,
 };
-
 uint idNext = Network + 1;
 
 struct Network network;
@@ -79,32 +78,33 @@ static void exitSave(void) {
 
 uint32_t hashInit;
 
-int utilPipe[2] = { -1, -1 };
+uint execID;
 int execPipe[2] = { -1, -1 };
+int utilPipe[2] = { -1, -1 };
 
-static void utilRead(void) {
+static void execRead(void) {
 	char buf[1024];
-	ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1);
+	ssize_t len = read(execPipe[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);
+		if (line[0]) command(execID, line);
 	}
 }
 
-static void execRead(void) {
+static void utilRead(void) {
 	char buf[1024];
-	ssize_t len = read(execPipe[0], buf, sizeof(buf) - 1);
+	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]) command(execID, line);
+		if (line[0]) uiFormat(Network, Warm, NULL, "%s", line);
 	}
 }
 
@@ -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(&params, " ");
-	if (!params) return;
-	commandPrivmsg(idFor(nick), params);
+	id = idFor(strsep(&params, " "));
+	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 <assert.h>
 #include <err.h>
 #include <errno.h>
+#include <limits.h>
 #include <regex.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -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");