about summary refs log tree commit diff
path: root/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'command.c')
-rw-r--r--command.c407
1 files changed, 290 insertions, 117 deletions
diff --git a/command.c b/command.c
index 4c51433..502ff17 100644
--- a/command.c
+++ b/command.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -64,8 +64,7 @@ static void echoMessage(char *cmd, uint id, char *params) {
 	handle(&msg);
 }
 
-static void splitMessage(char *cmd, uint id, char *params) {
-	if (!params) return;
+static int splitChunk(const char *cmd, uint id) {
 	int overhead = snprintf(
 		NULL, 0, ":%s!%*s@%*s %s %s :\r\n",
 		self.nick,
@@ -74,22 +73,32 @@ static void splitMessage(char *cmd, uint id, char *params) {
 		cmd, idNames[id]
 	);
 	assert(overhead > 0 && overhead < 512);
-	int chunk = 512 - overhead;
+	return 512 - overhead;
+}
+
+static int splitLen(int chunk, const char *params) {
+	int len = 0;
+	size_t cap = 1 + strlen(params);
+	for (int n = 0; params[len] != '\n' && len + n <= chunk; len += n) {
+		n = mblen(&params[len], cap - len);
+		if (n < 0) {
+			n = 1;
+			mblen(NULL, 0);
+		}
+		if (!n) break;
+	}
+	return len;
+}
+
+static void splitMessage(char *cmd, uint id, char *params) {
+	if (!params) return;
+	int chunk = splitChunk(cmd, id);
 	if (strlen(params) <= (size_t)chunk && !strchr(params, '\n')) {
 		echoMessage(cmd, id, params);
 		return;
 	}
-
 	while (*params) {
-		int len = 0;
-		for (int n = 0; params[len] != '\n' && len + n <= chunk; len += n) {
-			n = mblen(&params[len], 1 + strlen(&params[len]));
-			if (n < 0) {
-				n = 1;
-				mblen(NULL, 0);
-			}
-			if (!n) break;
-		}
+		int len = splitLen(chunk, params);
 		char ch = params[len];
 		params[len] = '\0';
 		echoMessage(cmd, id, params);
@@ -109,25 +118,47 @@ static void commandNotice(uint id, char *params) {
 
 static void commandMe(uint id, char *params) {
 	char buf[512];
-	snprintf(buf, sizeof(buf), "\1ACTION %s\1", (params ?: ""));
-	echoMessage("PRIVMSG", id, buf);
+	if (!params) params = "";
+	int chunk = splitChunk("PRIVMSG \1ACTION\1", id);
+	if (strlen(params) <= (size_t)chunk && !strchr(params, '\n')) {
+		snprintf(buf, sizeof(buf), "\1ACTION %s\1", params);
+		echoMessage("PRIVMSG", id, buf);
+		return;
+	}
+	while (*params) {
+		int len = splitLen(chunk, params);
+		snprintf(buf, sizeof(buf), "\1ACTION %.*s\1", len, params);
+		echoMessage("PRIVMSG", id, buf);
+		params += len;
+		if (*params == '\n') params++;
+	}
 }
 
 static void commandMsg(uint id, char *params) {
-	id = idFor(strsep(&params, " "));
-	splitMessage("PRIVMSG", id, params);
+	if (!params) return;
+	char *nick = strsep(&params, " ");
+	uint msg = idFor(nick);
+	if (idColors[msg] == Default) {
+		idColors[msg] = completeColor(id, nick);
+	}
+	if (params) {
+		splitMessage("PRIVMSG", msg, params);
+	} else {
+		windowShow(windowFor(msg));
+	}
 }
 
 static void commandJoin(uint id, char *params) {
+	if (!params && id == Network) params = self.invited;
 	if (!params) params = idNames[id];
 	uint count = 1;
 	for (char *ch = params; *ch && *ch != ' '; ++ch) {
 		if (*ch == ',') count++;
 	}
 	ircFormat("JOIN %s\r\n", params);
-	replies.join += count;
-	replies.topic += count;
-	replies.names += count;
+	replies[ReplyJoin] += count;
+	replies[ReplyTopic] += count;
+	replies[ReplyNames] += count;
 }
 
 static void commandPart(uint id, char *params) {
@@ -145,8 +176,14 @@ static void commandQuit(uint id, char *params) {
 
 static void commandNick(uint id, char *params) {
 	(void)id;
-	if (!params) return;
-	ircFormat("NICK :%s\r\n", params);
+	if (params) {
+		ircFormat("NICK :%s\r\n", params);
+	} else {
+		uiFormat(
+			Network, Warm, NULL, "You are \3%02d%s",
+			self.color, self.nick
+		);
+	}
 }
 
 static void commandAway(uint id, char *params) {
@@ -156,7 +193,13 @@ static void commandAway(uint id, char *params) {
 	} else {
 		ircFormat("AWAY\r\n");
 	}
-	replies.away++;
+	replies[ReplyAway]++;
+}
+
+static void commandSetname(uint id, char *params) {
+	(void)id;
+	if (!params) return;
+	ircFormat("SETNAME :%s\r\n", params);
 }
 
 static void commandTopic(uint id, char *params) {
@@ -164,14 +207,43 @@ static void commandTopic(uint id, char *params) {
 		ircFormat("TOPIC %s :%s\r\n", idNames[id], params);
 	} else {
 		ircFormat("TOPIC %s\r\n", idNames[id]);
-		replies.topic++;
+		replies[ReplyTopic]++;
 	}
 }
 
 static void commandNames(uint id, char *params) {
 	(void)params;
 	ircFormat("NAMES %s\r\n", idNames[id]);
-	replies.names++;
+	replies[ReplyNames]++;
+}
+
+static void commandOps(uint id, char *params) {
+	(void)params;
+	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) {
@@ -196,14 +268,20 @@ static void commandMode(uint id, char *params) {
 			ircFormat("MODE %s %s\r\n", self.nick, params);
 		} else {
 			ircFormat("MODE %s\r\n", self.nick);
-			replies.mode++;
+			replies[ReplyMode]++;
 		}
 	} else {
 		if (params) {
+			if (!params[1] || (params[0] == '+' && !params[2])) {
+				char m = (params[0] == '+' ? params[1] : params[0]);
+				if (m == 'b') replies[ReplyBan]++;
+				if (m == network.excepts) replies[ReplyExcepts]++;
+				if (m == network.invex) replies[ReplyInvex]++;
+			}
 			ircFormat("MODE %s %s\r\n", idNames[id], params);
 		} else {
 			ircFormat("MODE %s\r\n", idNames[id]);
-			replies.mode++;
+			replies[ReplyMode]++;
 		}
 	}
 }
@@ -221,7 +299,7 @@ static void commandOp(uint id, char *params) {
 	if (params) {
 		channelListMode(id, '+', 'o', params);
 	} else {
-		ircFormat("PRIVMSG ChanServ :OP %s\r\n", idNames[id]);
+		ircFormat("CS OP %s\r\n", idNames[id]);
 	}
 }
 
@@ -233,7 +311,7 @@ static void commandVoice(uint id, char *params) {
 	if (params) {
 		channelListMode(id, '+', 'v', params);
 	} else {
-		ircFormat("PRIVMSG ChanServ :VOICE %s\r\n", idNames[id]);
+		ircFormat("CS VOICE %s\r\n", idNames[id]);
 	}
 }
 
@@ -246,7 +324,7 @@ static void commandBan(uint id, char *params) {
 		channelListMode(id, '+', 'b', params);
 	} else {
 		ircFormat("MODE %s b\r\n", idNames[id]);
-		replies.ban++;
+		replies[ReplyBan]++;
 	}
 }
 
@@ -260,7 +338,7 @@ static void commandExcept(uint id, char *params) {
 		channelListMode(id, '+', network.excepts, params);
 	} else {
 		ircFormat("MODE %s %c\r\n", idNames[id], network.excepts);
-		replies.excepts++;
+		replies[ReplyExcepts]++;
 	}
 }
 
@@ -274,7 +352,7 @@ static void commandInvex(uint id, char *params) {
 		channelListMode(id, '+', network.invex, params);
 	} else {
 		ircFormat("MODE %s %c\r\n", idNames[id], network.invex);
-		replies.invex++;
+		replies[ReplyInvex]++;
 	}
 }
 
@@ -290,40 +368,65 @@ static void commandList(uint id, char *params) {
 	} else {
 		ircFormat("LIST\r\n");
 	}
-	replies.list++;
+	replies[ReplyList]++;
 }
 
 static void commandWhois(uint id, char *params) {
 	(void)id;
+	if (!params) params = self.nick;
+	uint count = 1;
+	for (char *ch = params; *ch; ++ch) {
+		if (*ch == ',') count++;
+	}
+	ircFormat("WHOIS %s\r\n", params);
+	replies[ReplyWhois] += count;
+}
+
+static void commandWhowas(uint id, char *params) {
+	(void)id;
 	if (!params) return;
-	ircFormat("WHOIS :%s\r\n", params);
-	replies.whois++;
+	ircFormat("WHOWAS %s\r\n", params);
+	replies[ReplyWhowas]++;
 }
 
 static void commandNS(uint id, char *params) {
 	(void)id;
-	if (params) ircFormat("PRIVMSG NickServ :%s\r\n", params);
+	ircFormat("NS %s\r\n", (params ?: "HELP"));
 }
 
 static void commandCS(uint id, char *params) {
 	(void)id;
-	if (params) ircFormat("PRIVMSG ChanServ :%s\r\n", params);
+	ircFormat("CS %s\r\n", (params ?: "HELP"));
 }
 
 static void commandQuery(uint id, char *params) {
 	if (!params) return;
 	uint query = idFor(params);
-	idColors[query] = completeColor(id, params);
-	uiShowID(query);
+	if (idColors[query] == Default) {
+		idColors[query] = completeColor(id, params);
+	}
+	windowShow(windowFor(query));
 }
 
 static void commandWindow(uint id, char *params) {
-	if (!params) return;
-	if (isdigit(params[0])) {
-		uiShowNum(strtoul(params, NULL, 10));
+	if (!params) {
+		windowList();
+	} else if (isdigit(params[0])) {
+		windowShow(strtoul(params, NULL, 10));
 	} else {
 		id = idFind(params);
-		if (id) uiShowID(id);
+		if (id) {
+			windowShow(windowFor(id));
+			return;
+		}
+		struct Cursor curs = {0};
+		for (const char *str; (str = completeSubstr(&curs, None, params));) {
+			id = idFind(str);
+			if (!id) continue;
+			completeAccept(&curs);
+			windowShow(windowFor(id));
+			break;
+		}
 	}
 }
 
@@ -332,20 +435,20 @@ static void commandMove(uint id, char *params) {
 	char *name = strsep(&params, " ");
 	if (params) {
 		id = idFind(name);
-		if (id) uiMoveID(id, strtoul(params, NULL, 10));
+		if (id) windowMove(windowFor(id), strtoul(params, NULL, 10));
 	} else {
-		uiMoveID(id, strtoul(name, NULL, 10));
+		windowMove(windowFor(id), strtoul(name, NULL, 10));
 	}
 }
 
 static void commandClose(uint id, char *params) {
 	if (!params) {
-		uiCloseID(id);
+		windowClose(windowFor(id));
 	} else if (isdigit(params[0])) {
-		uiCloseNum(strtoul(params, NULL, 10));
+		windowClose(strtoul(params, NULL, 10));
 	} else {
 		id = idFind(params);
-		if (id) uiCloseID(id);
+		if (id) windowClose(windowFor(id));
 	}
 }
 
@@ -363,33 +466,50 @@ static void commandCopy(uint id, char *params) {
 	urlCopyMatch(id, params);
 }
 
-static void commandIgnore(uint id, char *params) {
+static void commandFilter(enum Heat heat, uint id, char *params) {
 	if (params) {
-		const char *pattern = ignoreAdd(params);
+		struct Filter filter = filterAdd(heat, params);
 		uiFormat(
-			id, Cold, NULL, "Ignoring \3%02d%s\3",
-			Brown, pattern
+			id, Cold, NULL, "%sing \3%02d%s %s %s %s",
+			(heat == Hot ? "Highlight" : "Ignor"), Brown, filter.mask,
+			(filter.cmd ?: ""), (filter.chan ?: ""), (filter.mesg ?: "")
 		);
 	} else {
-		for (size_t i = 0; i < ignore.len; ++i) {
+		for (size_t i = 0; i < FilterCap && filters[i].mask; ++i) {
+			if (filters[i].heat != heat) continue;
 			uiFormat(
-				Network, Warm, NULL, "Ignoring \3%02d%s\3",
-				Brown, ignore.patterns[i]
+				Network, Warm, NULL, "%sing \3%02d%s %s %s %s",
+				(heat == Hot ? "Highlight" : "Ignor"), Brown, filters[i].mask,
+				(filters[i].cmd ?: ""), (filters[i].chan ?: ""),
+				(filters[i].mesg ?: "")
 			);
 		}
 	}
 }
 
-static void commandUnignore(uint id, char *params) {
+static void commandUnfilter(enum Heat heat, uint id, char *params) {
 	if (!params) return;
-	if (ignoreRemove(params)) {
-		uiFormat(
-			id, Cold, NULL, "No longer ignoring \3%02d%s\3",
-			Brown, params
-		);
-	} else {
-		uiFormat(id, Cold, NULL, "Not ignoring \3%02d%s\3", Brown, params);
-	}
+	struct Filter filter = filterParse(heat, params);
+	bool found = filterRemove(filter);
+	uiFormat(
+		id, Cold, NULL, "%s %sing \3%02d%s %s %s %s",
+		(found ? "No longer" : "Not"), (heat == Hot ? "highlight" : "ignor"),
+		Brown, filter.mask, (filter.cmd ?: ""), (filter.chan ?: ""),
+		(filter.mesg ?: "")
+	);
+}
+
+static void commandHighlight(uint id, char *params) {
+	commandFilter(Hot, id, params);
+}
+static void commandIgnore(uint id, char *params) {
+	commandFilter(Ice, id, params);
+}
+static void commandUnhighlight(uint id, char *params) {
+	commandUnfilter(Hot, id, params);
+}
+static void commandUnignore(uint id, char *params) {
+	commandUnfilter(Ice, id, params);
 }
 
 static void commandExec(uint id, char *params) {
@@ -399,26 +519,37 @@ static void commandExec(uint id, char *params) {
 	if (pid < 0) err(EX_OSERR, "fork");
 	if (pid) return;
 
+	setsid();
 	close(STDIN_FILENO);
 	dup2(execPipe[1], STDOUT_FILENO);
 	dup2(utilPipe[1], STDERR_FILENO);
 
 	const char *shell = getenv("SHELL") ?: "/bin/sh";
-	execlp(shell, shell, "-c", params, NULL);
+	execl(shell, shell, "-c", params, NULL);
 	warn("%s", shell);
 	_exit(EX_UNAVAILABLE);
 }
 
 static void commandHelp(uint id, char *params) {
 	(void)id;
-	uiHide();
 
+	if (params) {
+		ircFormat("HELP :%s\r\n", params);
+		replies[ReplyHelp]++;
+		return;
+	}
+	if (self.restricted) {
+		uiFormat(id, Warm, NULL, "See catgirl(1) or /help index");
+		return;
+	}
+
+	uiHide();
 	pid_t pid = fork();
 	if (pid < 0) err(EX_OSERR, "fork");
 	if (pid) return;
 
 	char buf[256];
-	snprintf(buf, sizeof(buf), "ip%s$", (params ?: "COMMANDS"));
+	snprintf(buf, sizeof(buf), "%sp^COMMANDS$", (getenv("LESS") ?: ""));
 	setenv("LESS", buf, 1);
 	execlp("man", "man", "1", "catgirl", NULL);
 	dup2(utilPipe[1], STDERR_FILENO);
@@ -428,55 +559,61 @@ static void commandHelp(uint id, char *params) {
 
 enum Flag {
 	BIT(Multiline),
-	BIT(Restricted),
+	BIT(Restrict),
 };
 
 static const struct Handler {
 	const char *cmd;
 	Command *fn;
 	enum Flag flags;
+	enum Cap caps;
 } Commands[] = {
-	{ "/away", commandAway, 0 },
-	{ "/ban", commandBan, 0 },
-	{ "/close", commandClose, 0 },
-	{ "/copy", commandCopy, Restricted },
-	{ "/cs", commandCS, 0 },
-	{ "/debug", commandDebug, Restricted },
-	{ "/deop", commandDeop, 0 },
-	{ "/devoice", commandDevoice, 0 },
-	{ "/except", commandExcept, 0 },
-	{ "/exec", commandExec, Multiline | Restricted },
-	{ "/help", commandHelp, 0 },
-	{ "/ignore", commandIgnore, 0 },
-	{ "/invex", commandInvex, 0 },
-	{ "/invite", commandInvite, 0 },
-	{ "/join", commandJoin, Restricted },
-	{ "/kick", commandKick, 0 },
-	{ "/list", commandList, 0 },
-	{ "/me", commandMe, 0 },
-	{ "/mode", commandMode, 0 },
-	{ "/move", commandMove, 0 },
-	{ "/msg", commandMsg, Multiline | Restricted },
-	{ "/names", commandNames, 0 },
-	{ "/nick", commandNick, 0 },
-	{ "/notice", commandNotice, Multiline },
-	{ "/ns", commandNS, 0 },
-	{ "/o", commandOpen, Restricted },
-	{ "/op", commandOp, 0 },
-	{ "/open", commandOpen, Restricted },
-	{ "/part", commandPart, 0 },
-	{ "/query", commandQuery, Restricted },
-	{ "/quit", commandQuit, 0 },
-	{ "/quote", commandQuote, Multiline | Restricted },
-	{ "/say", commandPrivmsg, Multiline },
-	{ "/topic", commandTopic, 0 },
-	{ "/unban", commandUnban, 0 },
-	{ "/unexcept", commandUnexcept, 0 },
-	{ "/unignore", commandUnignore, 0 },
-	{ "/uninvex", commandUninvex, 0 },
-	{ "/voice", commandVoice, 0 },
-	{ "/whois", commandWhois, 0 },
-	{ "/window", commandWindow, 0 },
+	{ "/away", commandAway, 0, 0 },
+	{ "/ban", commandBan, 0, 0 },
+	{ "/close", commandClose, 0, 0 },
+	{ "/copy", commandCopy, Restrict, 0 },
+	{ "/cs", commandCS, 0, 0 },
+	{ "/debug", commandDebug, 0, 0 },
+	{ "/deop", commandDeop, 0, 0 },
+	{ "/devoice", commandDevoice, 0, 0 },
+	{ "/except", commandExcept, 0, 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, 0, 0 },
+	{ "/kick", commandKick, 0, 0 },
+	{ "/list", commandList, 0, 0 },
+	{ "/me", commandMe, Multiline, 0 },
+	{ "/mode", commandMode, 0, 0 },
+	{ "/move", commandMove, 0, 0 },
+	{ "/msg", commandMsg, Multiline, 0 },
+	{ "/names", commandNames, 0, 0 },
+	{ "/nick", commandNick, 0, 0 },
+	{ "/notice", commandNotice, Multiline, 0 },
+	{ "/ns", commandNS, 0, 0 },
+	{ "/o", commandOpen, Restrict, 0 },
+	{ "/op", commandOp, 0, 0 },
+	{ "/open", commandOpen, Restrict, 0 },
+	{ "/ops", commandOps, 0, 0 },
+	{ "/part", commandPart, 0, 0 },
+	{ "/query", commandQuery, 0, 0 },
+	{ "/quit", commandQuit, 0, 0 },
+	{ "/quote", commandQuote, Multiline, 0 },
+	{ "/say", commandPrivmsg, Multiline, 0 },
+	{ "/setname", commandSetname, 0, CapSetname },
+	{ "/topic", commandTopic, 0, 0 },
+	{ "/unban", commandUnban, 0, 0 },
+	{ "/unexcept", commandUnexcept, 0, 0 },
+	{ "/unhighlight", commandUnhighlight, 0, 0 },
+	{ "/unignore", commandUnignore, 0, 0 },
+	{ "/uninvex", commandUninvex, 0, 0 },
+	{ "/voice", commandVoice, 0, 0 },
+	{ "/whois", commandWhois, 0, 0 },
+	{ "/whowas", commandWhowas, 0, 0 },
+	{ "/window", commandWindow, 0, 0 },
 };
 
 static int compar(const void *cmd, const void *_handler) {
@@ -505,6 +642,41 @@ const char *commandIsAction(uint id, const char *input) {
 	return &input[4];
 }
 
+size_t commandWillSplit(uint id, const char *input) {
+	int chunk;
+	const char *params;
+	if (NULL != (params = commandIsPrivmsg(id, input))) {
+		chunk = splitChunk("PRIVMSG", id);
+	} else if (NULL != (params = commandIsNotice(id, input))) {
+		chunk = splitChunk("NOTICE", id);
+	} else if (NULL != (params = commandIsAction(id, input))) {
+		chunk = splitChunk("PRIVMSG \1ACTION\1", id);
+	} else if (id != Network && id != Debug && !strncmp(input, "/say ", 5)) {
+		params = &input[5];
+		chunk = splitChunk("PRIVMSG", id);
+	} else {
+		return 0;
+	}
+	if (strlen(params) <= (size_t)chunk) return 0;
+	for (
+		int split;
+		params[(split = splitLen(chunk, params))];
+		params = &params[split + 1]
+	) {
+		if (params[split] == '\n') continue;
+		return (params - input) + split;
+	}
+	return 0;
+}
+
+static bool commandAvailable(const struct Handler *handler) {
+	if (handler->flags & Restrict && self.restricted) return false;
+	if (handler->caps && (handler->caps & self.caps) != handler->caps) {
+		return false;
+	}
+	return true;
+}
+
 void command(uint id, char *input) {
 	if (id == Debug && input[0] != '/' && !self.restricted) {
 		commandQuote(id, input);
@@ -519,11 +691,11 @@ void command(uint id, char *input) {
 		return;
 	}
 
+	struct Cursor curs = {0};
 	const char *cmd = strsep(&input, " ");
-	const char *unique = complete(None, cmd);
-	if (unique && !complete(None, cmd)) {
+	const char *unique = completePrefix(&curs, None, cmd);
+	if (unique && !completePrefix(&curs, None, cmd)) {
 		cmd = unique;
-		completeReject();
 	}
 
 	const struct Handler *handler = bsearch(
@@ -533,8 +705,8 @@ void command(uint id, char *input) {
 		uiFormat(id, Warm, NULL, "No such command %s", cmd);
 		return;
 	}
-	if (self.restricted && handler->flags & Restricted) {
-		uiFormat(id, Warm, NULL, "Command %s is restricted", cmd);
+	if (!commandAvailable(handler)) {
+		uiFormat(id, Warm, NULL, "Command %s is unavailable", cmd);
 		return;
 	}
 
@@ -550,8 +722,9 @@ void command(uint id, char *input) {
 	handler->fn(id, input);
 }
 
-void commandCompleteAdd(void) {
+void commandCompletion(void) {
 	for (size_t i = 0; i < ARRAY_LEN(Commands); ++i) {
-		completeAdd(None, Commands[i].cmd, Default);
+		if (!commandAvailable(&Commands[i])) continue;
+		completePush(None, Commands[i].cmd, Default);
 	}
 }