summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--catgirl.19
-rw-r--r--chat.h4
-rw-r--r--command.c11
-rw-r--r--handle.c15
-rw-r--r--url.c96
6 files changed, 133 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 48aba7b..bcbb0d8 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ OBJS += edit.o
OBJS += handle.o
OBJS += irc.o
OBJS += ui.o
+OBJS += url.o
dev: tags all
diff --git a/catgirl.1 b/catgirl.1
index 5394d33..f489d07 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -156,6 +156,15 @@ Close the named, numbered or current window.
Toggle logging in the
.Sy <debug>
window.
+.It Ic /open Op Ar count
+Open each of
+.Ar count
+most recent URLs.
+.It Ic /open Ar nick | substring
+Open the most recent URL from
+.Ar nick
+or matching
+.Ar substring .
.It Ic /window Ar name
Switch to window by name.
.It Ic /window Ar num , Ic / Ns Ar num
diff --git a/chat.h b/chat.h
index 909527e..583107a 100644
--- a/chat.h
+++ b/chat.h
@@ -169,6 +169,10 @@ void completeClear(size_t id);
size_t completeID(const char *str);
enum Color completeColor(size_t id, const char *str);
+void urlScan(size_t id, const char *nick, const char *mesg);
+void urlOpenCount(size_t id, size_t count);
+void urlOpenMatch(size_t id, const char *str);
+
FILE *configOpen(const char *path, const char *mode);
int getopt_config(
int argc, char *const *argv,
diff --git a/command.c b/command.c
index eaabc9c..4100928 100644
--- a/command.c
+++ b/command.c
@@ -144,6 +144,16 @@ static void commandClose(size_t id, char *params) {
}
}
+static void commandOpen(size_t id, char *params) {
+ if (!params) {
+ urlOpenCount(id, 1);
+ } else if (isdigit(params[0])) {
+ urlOpenCount(id, strtoul(params, NULL, 10));
+ } else {
+ urlOpenMatch(id, params);
+ }
+}
+
static const struct Handler {
const char *cmd;
Command *fn;
@@ -155,6 +165,7 @@ static const struct Handler {
{ "/names", commandNames },
{ "/nick", commandNick },
{ "/notice", commandNotice },
+ { "/open", commandOpen },
{ "/part", commandPart },
{ "/query", commandQuery },
{ "/quit", commandQuit },
diff --git a/handle.c b/handle.c
index 0780767..f919fcb 100644
--- a/handle.c
+++ b/handle.c
@@ -193,6 +193,7 @@ static void handleReplyISupport(struct Message *msg) {
static void handleReplyMOTD(struct Message *msg) {
require(msg, false, 2);
char *line = msg->params[1];
+ urlScan(Network, msg->nick, line);
if (!strncmp(line, "- ", 2)) {
uiFormat(Network, Cold, tagTime(msg), "\3%d-\3\t%s", Gray, &line[2]);
} else {
@@ -227,6 +228,7 @@ static void handlePart(struct Message *msg) {
completeClear(id);
}
completeRemove(id, msg->nick);
+ urlScan(id, msg->nick, msg->params[1]);
uiFormat(
id, Cold, tagTime(msg),
"\3%02d%s\3\tleaves \3%02d%s\3%s%s",
@@ -241,6 +243,7 @@ static void handleKick(struct Message *msg) {
size_t id = idFor(msg->params[0]);
bool kicked = self.nick && !strcmp(msg->params[1], self.nick);
completeTouch(id, msg->nick, hash(msg->user));
+ urlScan(id, msg->nick, msg->params[2]);
uiFormat(
id, (kicked ? Hot : Cold), tagTime(msg),
"%s\3%02d%s\17\tkicks \3%02d%s\3 out of \3%02d%s\3%s%s",
@@ -275,6 +278,7 @@ static void handleQuit(struct Message *msg) {
require(msg, true, 0);
size_t id;
while (None != (id = completeID(msg->nick))) {
+ urlScan(id, msg->nick, msg->params[0]);
uiFormat(
id, Cold, tagTime(msg),
"\3%02d%s\3\tleaves%s%s",
@@ -333,8 +337,10 @@ static void handleReplyTopic(struct Message *msg) {
require(msg, false, 3);
if (!replies.topic) return;
replies.topic--;
+ size_t id = idFor(msg->params[1]);
+ urlScan(id, NULL, msg->params[2]);
uiFormat(
- idFor(msg->params[1]), Cold, tagTime(msg),
+ id, Cold, tagTime(msg),
"The sign in \3%02d%s\3 reads: %s",
hash(msg->params[1]), msg->params[1], msg->params[2]
);
@@ -342,16 +348,18 @@ static void handleReplyTopic(struct Message *msg) {
static void handleTopic(struct Message *msg) {
require(msg, true, 2);
+ size_t id = idFor(msg->params[0]);
if (msg->params[1][0]) {
+ urlScan(id, msg->nick, msg->params[1]);
uiFormat(
- idFor(msg->params[0]), Warm, tagTime(msg),
+ id, Warm, tagTime(msg),
"\3%02d%s\3\tplaces a new sign in \3%02d%s\3: %s",
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0],
msg->params[1]
);
} else {
uiFormat(
- idFor(msg->params[0]), Warm, tagTime(msg),
+ id, Warm, tagTime(msg),
"\3%02d%s\3\tremoves the sign in \3%02d%s\3",
hash(msg->user), msg->nick, hash(msg->params[0]), msg->params[0]
);
@@ -400,6 +408,7 @@ static void handlePrivmsg(struct Message *msg) {
bool action = isAction(msg);
bool mention = !mine && isMention(msg);
if (!notice && !mine) completeTouch(id, msg->nick, hash(msg->user));
+ urlScan(id, msg->nick, msg->params[1]);
if (notice) {
uiFormat(
id, Warm, tagTime(msg),
diff --git a/url.c b/url.c
new file mode 100644
index 0000000..7790461
--- /dev/null
+++ b/url.c
@@ -0,0 +1,96 @@
+/* Copyright (C) 2020 C. 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
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <err.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "chat.h"
+
+static const char *Pattern = {
+ "("
+ "cvs|"
+ "ftp|"
+ "git|"
+ "gopher|"
+ "http|"
+ "https|"
+ "irc|"
+ "ircs|"
+ "magnet|"
+ "sftp|"
+ "ssh|"
+ "svn|"
+ "telnet|"
+ "vnc"
+ ")"
+ ":[^[:space:]>\"]+"
+};
+static regex_t Regex;
+
+static void compile(void) {
+ static bool compiled;
+ if (compiled) return;
+ compiled = true;
+ int error = regcomp(&Regex, Pattern, REG_EXTENDED);
+ if (!error) return;
+ char buf[256];
+ regerror(error, &Regex, buf, sizeof(buf));
+ errx(EX_SOFTWARE, "regcomp: %s: %s", buf, Pattern);
+}
+
+enum { Cap = 32 };
+static struct {
+ size_t ids[Cap];
+ char *nicks[Cap];
+ char *urls[Cap];
+ size_t len;
+} ring;
+
+static void push(size_t id, const char *nick, const char *url, size_t len) {
+ size_t i = ring.len++ % Cap;
+ free(ring.nicks[i]);
+ free(ring.urls[i]);
+ ring.ids[i] = id;
+ ring.nicks[i] = NULL;
+ if (nick) {
+ ring.nicks[i] = strdup(nick);
+ if (!ring.nicks[i]) err(EX_OSERR, "strdup");
+ }
+ ring.urls[i] = strndup(url, len);
+ if (!ring.urls[i]) err(EX_OSERR, "strndup");
+}
+
+void urlScan(size_t id, const char *nick, const char *mesg) {
+ if (!mesg) return;
+ compile();
+ regmatch_t match = {0};
+ for (const char *ptr = mesg; *ptr; ptr += match.rm_eo) {
+ if (regexec(&Regex, ptr, 1, &match, 0)) break;
+ push(id, nick, &ptr[match.rm_so], match.rm_eo - match.rm_so);
+ }
+}
+
+void urlOpenCount(size_t id, size_t count) {
+ // TODO
+}
+
+void urlOpenMatch(size_t id, const char *str) {
+ // TODO
+}