about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-02-08 21:44:50 -0500
committerJune McEnroe <june@causal.agency>2020-02-08 21:44:50 -0500
commit3e6868414811be8902e6973c78ef2010b26a9e08 (patch)
treedf68dcfb29b6fb0c68b3d96a599a4051097f9fe9
parentAllow overriding the /open utility (diff)
downloadcatgirl-3e6868414811be8902e6973c78ef2010b26a9e08.tar.gz
catgirl-3e6868414811be8902e6973c78ef2010b26a9e08.zip
Add /copy
-rw-r--r--catgirl.117
-rw-r--r--chat.c4
-rw-r--r--chat.h2
-rw-r--r--command.c5
-rw-r--r--url.c57
5 files changed, 83 insertions, 2 deletions
diff --git a/catgirl.1 b/catgirl.1
index 6129b71..4dabb4f 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -9,6 +9,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl ev
+.Op Fl C Ar copy
 .Op Fl O Ar open
 .Op Fl a Ar auth
 .Op Fl c Ar cert
@@ -47,8 +48,17 @@ following their corresponding flags.
 .Pp
 The arguments are as follows:
 .Bl -tag -width Ds
+.It Fl C Ar util , Cm copy = Ar util
+Set the utility used by
+.Ic /copy .
+The default is the first available of
+.Xr pbcopy 1 ,
+.Xr wl-copy 1 ,
+.Xr xclip 1 ,
+.Xr xsel 1 .
+.
 .It Fl O Ar util , Cm open = Ar util
-Set the command used by
+Set the utility used by
 .Ic /open .
 The default is the first available of
 .Xr open 1 ,
@@ -160,6 +170,11 @@ Show or set the topic of the channel.
 .Bl -tag -width Ds
 .It Ic /close Op Ar name | num
 Close the named, numbered or current window.
+.It Ic /copy Op Ar nick | substring
+Copy the most recent URL from
+.Ar nick
+or matching
+.Ar substring .
 .It Ic /debug
 Toggle logging in the
 .Sy <debug>
diff --git a/chat.c b/chat.c
index 77aa61d..dbad242 100644
--- a/chat.c
+++ b/chat.c
@@ -81,9 +81,10 @@ int main(int argc, char *argv[]) {
 	const char *user = NULL;
 	const char *real = NULL;
 
-	const char *Opts = "!O:a:c:eh:j:k:n:p:r:u:vw:";
+	const char *Opts = "!C:O:a:c:eh:j:k:n:p:r:u:vw:";
 	const struct option LongOpts[] = {
 		{ "insecure", no_argument, NULL, '!' },
+		{ "copy", required_argument, NULL, 'C' },
 		{ "open", required_argument, NULL, 'O' },
 		{ "sasl-plain", required_argument, NULL, 'a' },
 		{ "cert", required_argument, NULL, 'c' },
@@ -104,6 +105,7 @@ int main(int argc, char *argv[]) {
 	while (0 < (opt = getopt_config(argc, argv, Opts, LongOpts, NULL))) {
 		switch (opt) {
 			break; case '!': insecure = true;
+			break; case 'C': urlCopyUtil = optarg;
 			break; case 'O': urlOpenUtil = optarg;
 			break; case 'a': sasl = true; self.plain = optarg;
 			break; case 'c': cert = optarg;
diff --git a/chat.h b/chat.h
index 3084359..8bc8e81 100644
--- a/chat.h
+++ b/chat.h
@@ -170,9 +170,11 @@ size_t completeID(const char *str);
 enum Color completeColor(size_t id, const char *str);
 
 extern const char *urlOpenUtil;
+extern const char *urlCopyUtil;
 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);
+void urlCopyMatch(size_t id, const char *str);
 
 FILE *configOpen(const char *path, const char *mode);
 int getopt_config(
diff --git a/command.c b/command.c
index 4100928..feb52b7 100644
--- a/command.c
+++ b/command.c
@@ -154,11 +154,16 @@ static void commandOpen(size_t id, char *params) {
 	}
 }
 
+static void commandCopy(size_t id, char *params) {
+	urlCopyMatch(id, params);
+}
+
 static const struct Handler {
 	const char *cmd;
 	Command *fn;
 } Commands[] = {
 	{ "/close", commandClose },
+	{ "/copy", commandCopy },
 	{ "/debug", commandDebug },
 	{ "/join", commandJoin },
 	{ "/me", commandMe },
diff --git a/url.c b/url.c
index c9c4d5c..7ab1e53 100644
--- a/url.c
+++ b/url.c
@@ -122,6 +122,47 @@ static void urlOpen(const char *url) {
 	_exit(EX_CONFIG);
 }
 
+const char *urlCopyUtil;
+static const char *CopyUtils[] = { "pbcopy", "wl-copy", "xclip", "xsel" };
+
+static void urlCopy(const char *url) {
+	int rw[2];
+	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");
+
+	error = close(rw[1]);
+	if (error) err(EX_IOERR, "close");
+
+	pid_t pid = fork();
+	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid) {
+		close(rw[0]);
+		return;
+	}
+
+	dup2(rw[0], STDIN_FILENO);
+	dup2(procPipe[1], STDOUT_FILENO);
+	dup2(procPipe[1], STDERR_FILENO);
+	close(rw[0]);
+	if (urlCopyUtil) {
+		execlp(urlCopyUtil, urlCopyUtil, NULL);
+		warn("%s", urlCopyUtil);
+		_exit(EX_CONFIG);
+	}
+	for (size_t i = 0; i < ARRAY_LEN(CopyUtils); ++i) {
+		execlp(CopyUtils[i], CopyUtils[i], NULL);
+		if (errno != ENOENT) {
+			warn("%s", CopyUtils[i]);
+			_exit(EX_CONFIG);
+		}
+	}
+	warnx("no copy utility found");
+	_exit(EX_CONFIG);
+}
+
 void urlOpenCount(size_t id, size_t count) {
 	for (size_t i = 1; i <= Cap; ++i) {
 		const struct URL *url = &ring.urls[(ring.len - i) % Cap];
@@ -143,3 +184,19 @@ void urlOpenMatch(size_t id, const char *str) {
 		}
 	}
 }
+
+void urlCopyMatch(size_t id, const char *str) {
+	for (size_t i = 1; i <= Cap; ++i) {
+		const struct URL *url = &ring.urls[(ring.len - i) % Cap];
+		if (!url->url) break;
+		if (url->id != id) continue;
+		if (
+			!str
+			|| (url->nick && !strcmp(url->nick, str))
+			|| strstr(url->url, str)
+		) {
+			urlCopy(url->url);
+			break;
+		}
+	}
+}