about summary refs log tree commit diff
path: root/handle.c
diff options
context:
space:
mode:
Diffstat (limited to 'handle.c')
-rw-r--r--handle.c218
1 files changed, 124 insertions, 94 deletions
diff --git a/handle.c b/handle.c
index 4389ff8..ed1ba35 100644
--- a/handle.c
+++ b/handle.c
@@ -15,6 +15,7 @@
  */
 
 #include <err.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -32,15 +33,8 @@ static int color(const char *s) {
 	return (x == 1) ? 0 : x;
 }
 
-typedef void (*Handler)(char *prefix, char *params);
-
-static char *prift(char **prefix) {
-	return strsep(prefix, "!@");
-}
-
-static char *shift(char **params) {
+static char *paramField(char **params) {
 	char *rest = *params;
-	if (!rest) errx(EX_PROTOCOL, "unexpected eol");
 	if (rest[0] == ':') {
 		*params = NULL;
 		return &rest[1];
@@ -48,24 +42,59 @@ static char *shift(char **params) {
 	return strsep(params, " ");
 }
 
+static void shift(
+	char *prefix, char **nick, char **user, char **host,
+	char *params, size_t req, size_t opt, /* (char **) */ ...
+) {
+	char *field;
+	if (prefix) {
+		field = strsep(&prefix, "!");
+		if (nick) *nick = field;
+		field = strsep(&prefix, "@");
+		if (user) *user = field;
+		if (host) *host = prefix;
+	}
+
+	va_list ap;
+	va_start(ap, opt);
+	for (size_t i = 0; i < req; ++i) {
+		if (!params) {
+			va_end(ap);
+			errx(EX_PROTOCOL, "%zu params required, found %zu", req, i);
+		}
+		field = paramField(&params);
+		char **param = va_arg(ap, char **);
+		if (param) *param = field;
+	}
+	for (size_t i = 0; i < opt; ++i) {
+		char **param = va_arg(ap, char **);
+		if (params) {
+			*param = paramField(&params);
+		} else {
+			*param = NULL;
+		}
+	}
+	va_end(ap);
+}
+
+typedef void (*Handler)(char *prefix, char *params);
+
 static void handlePing(char *prefix, char *params) {
 	(void)prefix;
 	ircFmt("PONG %s\r\n", params);
 }
 
 static void handle432(char *prefix, char *params) {
-	(void)prefix;
-	shift(&params);
-	shift(&params);
-	char *mesg = shift(&params);
+	char *mesg;
+	shift(prefix, NULL, NULL, NULL, params, 1, 0, &mesg);
 	uiLog(L"You can't use that name here");
 	uiFmt("Sheriff says, \"%s\"", mesg);
 	uiLog(L"Type /nick <name> to choose a new one");
 }
 
 static void handle001(char *prefix, char *params) {
-	(void)prefix;
-	char *nick = shift(&params);
+	char *nick;
+	shift(prefix, NULL, NULL, NULL, params, 1, 0, &nick);
 	if (strcmp(nick, chat.nick)) {
 		free(chat.nick);
 		chat.nick = strdup(nick);
@@ -74,27 +103,23 @@ static void handle001(char *prefix, char *params) {
 }
 
 static void handleJoin(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	char *chan = shift(&params);
+	char *nick, *user, *chan;
+	shift(prefix, &nick, &user, NULL, params, 1, 0, &chan);
+	uiFmt(
+		"\3%d%s\3 arrives in \3%d%s\3",
+		color(user), nick, color(chan), chan
+	);
 	if (!strcmp(nick, chat.nick) && strcmp(user, chat.user)) {
 		free(chat.user);
 		chat.user = strdup(user);
 	}
 	tabTouch(nick);
-	uiFmt(
-		"\3%d%s\3 arrives in \3%d%s\3",
-		color(user), nick, color(chan), chan
-	);
 }
 
 static void handlePart(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	char *chan = shift(&params);
-	tabRemove(nick);
-	if (params) {
-		char *mesg = shift(&params);
+	char *nick, *user, *chan, *mesg;
+	shift(prefix, &nick, &user, NULL, params, 1, 1, &chan, &mesg);
+	if (mesg) {
 		uiFmt(
 			"\3%d%s\3 leaves \3%d%s\3, \"%s\"",
 			color(user), nick, color(chan), chan, mesg
@@ -105,14 +130,13 @@ static void handlePart(char *prefix, char *params) {
 			color(user), nick, color(chan), chan
 		);
 	}
+	tabRemove(nick);
 }
 
 static void handleQuit(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	tabRemove(nick);
-	if (params) {
-		char *mesg = shift(&params);
+	char *nick, *user, *mesg;
+	shift(prefix, &nick, &user, NULL, params, 0, 1, &mesg);
+	if (mesg) {
 		char *quot = (mesg[0] == '"') ? "" : "\"";
 		uiFmt(
 			"\3%d%s\3 leaves, %s%s%s",
@@ -121,51 +145,51 @@ static void handleQuit(char *prefix, char *params) {
 	} else {
 		uiFmt("\3%d%s\3 leaves", color(user), nick);
 	}
+	tabRemove(nick);
 }
 
 static void handleKick(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	char *chan = shift(&params);
-	char *kick = shift(&params);
-	char *mesg = shift(&params);
+	char *nick, *user, *chan, *kick, *mesg;
+	shift(prefix, &nick, &user, NULL, params, 2, 1, &chan, &kick, &mesg);
+	if (mesg) {
+		uiFmt(
+			"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"",
+			color(user), nick, color(kick), kick, color(chan), chan, mesg
+		);
+	} else {
+		uiFmt(
+			"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3",
+			color(user), nick, color(kick), kick, color(chan), chan
+		);
+	}
 	tabRemove(nick);
-	uiFmt(
-		"\3%d%s\3 kicks \3%d%s\3 out of \3%d%s\3, \"%s\"",
-		color(user), nick, color(kick), kick, color(chan), chan, mesg
-	);
 }
 
 static void handle332(char *prefix, char *params) {
-	(void)prefix;
-	shift(&params);
-	char *chan = shift(&params);
-	char *topic = shift(&params);
-	urlScan(topic);
-	uiTopicStr(topic);
+	char *chan, *topic;
+	shift(prefix, NULL, NULL, NULL, params, 3, 0, NULL, &chan, &topic);
 	uiFmt(
 		"The sign in \3%d%s\3 reads, \"%s\"",
 		color(chan), chan, topic
 	);
+	urlScan(topic);
+	uiTopicStr(topic);
 }
 
 static void handleTopic(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	char *chan = shift(&params);
-	char *topic = shift(&params);
-	urlScan(topic);
-	uiTopicStr(topic);
+	char *nick, *user, *chan, *topic;
+	shift(prefix, &nick, &user, NULL, params, 2, 0, &chan, &topic);
 	uiFmt(
 		"\3%d%s\3 places a new sign in \3%d%s\3, \"%s\"",
 		color(user), nick, color(chan), chan, topic
 	);
+	urlScan(topic);
+	uiTopicStr(topic);
 }
 
 static void handle366(char *prefix, char *params) {
-	(void)prefix;
-	shift(&params);
-	char *chan = shift(&params);
+	char *chan;
+	shift(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &chan);
 	ircFmt("WHO %s\r\n", chan);
 }
 
@@ -175,14 +199,11 @@ static struct {
 } who;
 
 static void handle352(char *prefix, char *params) {
-	(void)prefix;
-	shift(&params);
-	shift(&params);
-	char *user = shift(&params);
-	shift(&params);
-	shift(&params);
-	char *nick = shift(&params);
-	tabTouch(nick);
+	char *user, *nick;
+	shift(
+		prefix, NULL, NULL, NULL,
+		params, 6, 0, NULL, NULL, &user, NULL, NULL, &nick
+	);
 	size_t cap = sizeof(who.buf) - who.len;
 	int len = snprintf(
 		&who.buf[who.len], cap,
@@ -190,65 +211,74 @@ static void handle352(char *prefix, char *params) {
 		(who.len ? ", " : ""), color(user), nick
 	);
 	if ((size_t)len < cap) who.len += len;
+	tabTouch(nick);
 }
 
 static void handle315(char *prefix, char *params) {
-	(void)prefix;
-	shift(&params);
-	char *chan = shift(&params);
-	who.len = 0;
+	char *chan;
+	shift(prefix, NULL, NULL, NULL, params, 2, 0, NULL, &chan);
 	uiFmt(
 		"In \3%d%s\3 are %s",
 		color(chan), chan, who.buf
 	);
+	who.len = 0;
 }
 
 static void handleNick(char *prefix, char *params) {
-	char *prev = prift(&prefix);
-	char *user = prift(&prefix);
-	char *next = shift(&params);
+	char *prev, *user, *next;
+	shift(prefix, &prev, &user, NULL, params, 1, 0, &next);
+	uiFmt(
+		"\3%d%s\3 is now known as \3%d%s\3",
+		color(user), prev, color(user), next
+	);
 	if (!strcmp(user, chat.user)) {
 		free(chat.nick);
 		chat.nick = strdup(next);
 	}
 	tabReplace(prev, next);
+}
+
+static void handleCTCP(char *nick, char *user, char *mesg) {
+	mesg = &mesg[1];
+	char *ctcp = strsep(&mesg, " ");
+	char *params = strsep(&mesg, "\1");
+	if (strcmp(ctcp, "ACTION")) return;
 	uiFmt(
-		"\3%d%s\3 is now known as \3%d%s\3",
-		color(user), prev, color(user), next
+		"* \3%d%s\3 %s",
+		color(user), nick, params
 	);
+	if (strcmp(user, chat.user)) tabTouch(nick);
+	urlScan(params);
 }
 
 static void handlePrivmsg(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	shift(&params);
-	char *mesg = shift(&params);
+	char *nick, *user, *mesg;
+	shift(prefix, &nick, &user, NULL, params, 2, 0, NULL, &mesg);
+	if (mesg[0] == '\1') {
+		handleCTCP(nick, user, mesg);
+		return;
+	}
 	bool self = !strcmp(user, chat.user);
 	bool ping = !strncasecmp(mesg, chat.nick, strlen(chat.nick));
+	uiFmt(
+		"%c%c\3%d%s\17%c %s",
+		self["<["], ping["\17\26"], color(user), nick, self[">]"], mesg
+	);
 	if (!self) tabTouch(nick);
-	urlScan(mesg);
 	if (ping) uiBeep();
-	if (mesg[0] == '\1') {
-		strsep(&mesg, " ");
-		char *action = strsep(&mesg, "\1");
-		uiFmt("* \3%d%s\3 %s", color(user), nick, action);
-	} else {
-		uiFmt(
-			"%c%c\3%d%s\17%c %s",
-			self["<["], ping["\17\26"], color(user), nick, self[">]"], mesg
-		);
-	}
+	urlScan(mesg);
 }
 
 static void handleNotice(char *prefix, char *params) {
-	char *nick = prift(&prefix);
-	char *user = prift(&prefix);
-	char *chan = shift(&params);
-	char *mesg = shift(&params);
-	if (strcmp(chat.chan, chan)) return;
+	char *nick, *user, *chan, *mesg;
+	shift(prefix, &nick, &user, NULL, params, 2, 0, &chan, &mesg);
+	if (strcmp(chan, chat.chan)) return;
+	uiFmt(
+		"-\3%d%s\3- %s",
+		color(user), nick, mesg
+	);
 	tabTouch(nick);
 	urlScan(mesg);
-	uiFmt("-\3%d%s\3- %s", color(user), nick, mesg);
 }
 
 static const struct {