/* [ʞ] newtab.c
* ~ lexi hale <lexi@hale.su>
* $ cc -Ofast newtab.c -onewtab \
* [-D_default_qutebrowser_location=/...]
* $ ./newtab [example.net]
* © AGPLv3
* ? may god have mercy on my soul.
* i wrote this because qutebrowser, being a python
* abomination, takes an absurdly fucking long time
* to load, even when there's already an instance
* running and i just want a new goddamn tab. it
* turns out, qutebrowser publishes an inane IPC
* mechanism, in which JSON (i know!!) written to a
* unix domain socket (I KNOW) can send signals to
* a running qutebrowser process. newtab checks if
* there's a running qutebrowser process and sends
* the appropriate IPC signal if so. otherwise, it
* forks off a new qutebrowser instance and returns
* an exit status of 1.
*
* it takes a single argument, the URI of a page to
* navigate to. if launched without an argument,
* newtab simply opens a blank new tab. simple
* enough, right? but why write it in C? to talk to
* a goddammned python application, of all things?
* well, i was originally going to do this in a
* bash script with socat, but then i pulled up the
* socat manpage and immediately decided it would
* be faster to implement the damn thing in C, and
* performance would be better anyway. i think it
* is fair to say that i was correct.
*
* so, that's half the story. the code quality is
* the other half. i'm going to be straight with
* you: i wrote this sleep-deprived at 7am because
* i'd stayed up all night with my boyfriend who's
* still remote-working on German time because his
* employers are kind of assholes. i'm aware that's
* not an excuse and i apologize sincerely. if
* anyone wants to submit a MR to tidy up this
* abomination without sacrificing performance, i
* would welcome it gratefully. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <dirent.h>
#include <unistd.h>
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#define ssz(str) (str), (sizeof str)
#define dupl(x) x,x
#ifndef _default_qutebrowser_location
# define _default_qutebrowser_location "/usr/bin/qutebrowser"
#endif
#define jstr(val) "\"" val "\""
#define j(key, val) jstr(#key) ": " val
#define json_msg_start "{" \
j(protocol_version, "1") "," \
j(target_arg, jstr("")) "," \
jstr("args")": [\""
#define json_msg_end "\"]}\n"
enum status {
ok,
start_instance,
fail_xdg_null,
fail_find,
};
const char* errors [] = {
[ok] = NULL,
[start_instance] = NULL,
[fail_xdg_null] = "$XDG_RUNTIME_DIR is not set - cannot locate IPC socket",
[fail_find] = "cannot find qutebrowser - is your $PATH set correctly?",
};
enum status
transmit(const char* uri) {
const char* const run = getenv("XDG_RUNTIME_DIR");
if (run == NULL) return fail_xdg_null;
struct sockaddr_un srv = {
.sun_family = AF_UNIX,
} ; {
/* fuck this fuck this fuck this fuck this */
char* end = stpncpy(srv.sun_path, run, sizeof srv.sun_path);
end = stpncpy(end, ssz("/qutebrowser/"));
DIR* qb = opendir(srv.sun_path);
if (!qb) return start_instance;
struct dirent* ent;
while (ent = readdir(qb)) {
if (ent == NULL) return start_instance;
if (strncmp(ent -> d_name, "ipc-", 4) == 0) break;
}
if (ent == NULL) return start_instance;
end = stpncpy(end, ent->d_name,
(sizeof srv.sun_path) - (end - srv.sun_path));
closedir(qb);
}
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (connect(sock, (struct sockaddr*)&srv, sizeof srv) != 0)
return start_instance;
const char msg_blank [] = json_msg_start json_msg_end;
const size_t extra = uri != NULL ? strlen(uri) : 0;
char msg_start [(sizeof json_msg_start) + extra];
const size_t msgsz = sizeof msg_blank + extra - 1;
const char* msg;
if (uri == NULL) msg = msg_blank; else {
strcpy(msg_start, json_msg_start);
strcpy(msg_start + (sizeof json_msg_start) - 1, uri);
strcpy(msg_start + (sizeof json_msg_start) - 1 + extra, json_msg_end);
msg = msg_start;
}
const char* cur = msg;
while(cur < msg + msgsz)
cur += write(sock, cur, msgsz - (cur - msg));
close(sock);
return ok;
}
int main(int argc, char** argv) {
if (argc > 2) {
printf("\x1b[1musage:\x1b[m %s [uri]\n", argv[0]);
} else {
const char* uri = argc < 2 ? NULL : argv[1];
enum status st = transmit(uri);
if (st == start_instance) {
if (!fork()) {
execl(dupl(_default_qutebrowser_location), uri, NULL);
execl(dupl("/usr/local/bin/qutebrowser"), uri, NULL);
execlp(dupl("qutebrowser"), uri, NULL);
st = fail_find;
} else {
return start_instance;
}
}
if (errors[st] != NULL) printf("\x1b[1;31m(error)\x1b[m %s\n", errors[st]);
return st;
}
}