summary refs log tree commit diff
path: root/input.c
blob: 85c240fac8b1d7126cd84753c39518eb8a731fb2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
2020-03-09Import /usr/src/usr.bin/printf from FreeBSD 12.1-RELEASEJune McEnroe
2020-03-09Import /usr/src/bin/test from FreeBSD 12.1-RELEASEJune McEnroe
2020-03-09Import /usr/src/bin/kill from FreeBSD 12.1-RELEASEJune McEnroe
2020-03-09Remove extraneous files from sh sourcesJune McEnroe
2020-03-09Import /usr/src/bin/sh from FreeBSD 12.1-RELEASEJune McEnroe
2020-03-09Remove 1sh sources139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
/* Copyright (C) 2018  Curtis McEnroe <june@causal.agency>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>
#include <err.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>

#include "chat.h"

static void privmsg(struct Tag tag, bool action, const char *mesg) {
	if (tag.id == TagStatus.id || tag.id == TagVerbose.id) return;
	char *line;
	int send;
	asprintf(
		&line, ":%s!%s %nPRIVMSG %s :%s%s%s",
		self.nick, self.user, &send, tag.name,
		(action ? "\1ACTION " : ""), mesg, (action ? "\1" : "")
	);
	if (!line) err(EX_OSERR, "asprintf");
	ircFmt("%s\r\n", &line[send]);
	handle(line);
	free(line);
}

static char *param(const char *command, char **params, const char *name) {
	char *param = strsep(params, " ");
	if (param) return param;
	uiFmt(TagStatus, UIHot, "%s requires a %s", command, name);
	return NULL;
}

typedef void (*Handler)(struct Tag tag, char *params);

static void inputMe(struct Tag tag, char *params) {
	privmsg(tag, true, params ? params : "");
}

static void inputNick(struct Tag tag, char *params) {
	(void)tag;
	char *nick = param("/nick", &params, "name");
	if (!nick) return;
	ircFmt("NICK %s\r\n", nick);
}

static void inputJoin(struct Tag tag, char *params) {
	(void)tag;
	char *chan = param("/join", &params, "channel");
	if (!chan) return;
	ircFmt("JOIN %s\r\n", chan);
}

static void inputPart(struct Tag tag, char *params) {
	if (params) {
		ircFmt("PART %s :%s\r\n", tag.name, params);
	} else {
		ircFmt("PART %s :Goodbye\r\n", tag.name);
	}
}

static void inputQuery(struct Tag tag, char *params) {
	(void)tag;
	char *nick = param("/query", &params, "nick");
	if (!nick) return;
	tabTouch(TagNone, nick);
	uiViewTag(tagFor(nick));
}

static void inputWho(struct Tag tag, char *params) {
	(void)params;
	ircFmt("WHO %s\r\n", tag.name);
}

static void inputWhois(struct Tag tag, char *params) {
	(void)tag;
	char *nick = param("/whois", &params, "nick");
	if (!nick) return;
	ircFmt("WHOIS %s\r\n", nick);
}

static void inputTopic(struct Tag tag, char *params) {
	if (params) {
		ircFmt("TOPIC %s :%s\r\n", tag.name, params);
	} else {
		ircFmt("TOPIC %s\r\n", tag.name);
	}
}

static void inputQuit(struct Tag tag, char *params) {
	(void)tag;
	if (params) {
		ircFmt("QUIT :%s\r\n", params);
	} else {
		ircFmt("QUIT :Goodbye\r\n");
	}
}

static void inputURL(struct Tag tag, char *params) {
	(void)params;
	urlList(tag);
}
static void inputOpen(struct Tag tag, char *params) {
	if (params && !isdigit(params[0])) {
		urlOpenMatch(tag, params);
	} else {
		size_t at = (params ? strtoul(strsep(&params, "-,"), NULL, 0) : 1);
		size_t to = (params ? strtoul(params, NULL, 0) : at);
		urlOpenRange(tag, at - 1, to);
	}
}

static void inputView(struct Tag tag, char *params) {
	(void)tag;
	char *view = param("/view", &params, "name or number");
	if (!view) return;
	int num = strtol(view, &view, 0);
	if (!view[0]) {
		uiViewNum(num);
	} else {
		struct Tag tag = tagFind(view);
		if (tag.id != TagNone.id) {
			uiViewTag(tag);
		} else {
			uiFmt(TagStatus, UIHot, "No view for %s", view);
		}
	}
}

static void inputClose(struct Tag tag, char *params) {
	(void)params;
	uiCloseTag(tag);
	tabRemove(TagNone, tag.name);
}

static void inputMan(struct Tag tag, char *params) {
	(void)tag;
	(void)params;
	eventWait((const char *[]) { "man", "1", "catgirl", NULL });
}

static const struct {
	const char *command;
	Handler handler;
} Commands[] = {
	{ "/close", inputClose },
	{ "/help", inputMan },
	{ "/join", inputJoin },
	{ "/man", inputMan },
	{ "/me", inputMe },
	{ "/names", inputWho },
	{ "/nick", inputNick },
	{ "/open", inputOpen },
	{ "/part", inputPart },
	{ "/query", inputQuery },
	{ "/quit", inputQuit },
	{ "/topic", inputTopic },
	{ "/url", inputURL },
	{ "/view", inputView },
	{ "/who", inputWho },
	{ "/whois", inputWhois },
};
static const size_t CommandsLen = sizeof(Commands) / sizeof(Commands[0]);

void input(struct Tag tag, char *input) {
	bool slash = (input[0] == '/');
	if (slash) {
		char *space = strchr(&input[1], ' ');
		char *extra = strchr(&input[1], '/');
		if (extra && (!space || extra < space)) slash = false;
	}

	if (!slash) {
		if (tag.id == TagVerbose.id) {
			ircFmt("%s\r\n", input);
		} else {
			privmsg(tag, false, input);
		}
		return;
	}

	char *word = strsep(&input, " ");
	if (input && !input[0]) input = NULL;

	char *trail;
	strtol(&word[1], &trail, 0);
	if (!trail[0]) {
		inputView(tag, &word[1]);
		return;
	}

	const char *command = word;
	const char *uniq = tabNext(TagNone, command);
	if (uniq && uniq == tabNext(TagNone, command)) {
		command = uniq;
		tabAccept();
	} else {
		tabReject();
	}

	for (size_t i = 0; i < CommandsLen; ++i) {
		if (strcasecmp(command, Commands[i].command)) continue;
		Commands[i].handler(tag, input);
		return;
	}
	uiFmt(TagStatus, UIHot, "%s isn't a recognized command", command);
}

void inputTab(void) {
	for (size_t i = 0; i < CommandsLen; ++i) {
		tabTouch(TagNone, Commands[i].command);
	}
}