util  Check-in [6b40270f28]

Overview
Comment:add newtab
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6b40270f2843f2cbfc588dc51f60abcdaaeb1649f2aa3c97a31361c8811eca93
User & Date: lexi on 2019-11-05 16:14:39
Other Links: manifest | tags
Context
2019-11-05
16:27
nitpick check-in: 2a975fabe3 user: lexi tags: trunk
16:14
add newtab check-in: 6b40270f28 user: lexi tags: trunk
2019-10-17
20:03
reorganize dumbass directory structure check-in: c29fe4718a user: lexi tags: trunk
Changes

Added newtab.c version [bf8e7ec053].

            1  +/* [ʞ] newtab.c
            2  + *  ~ lexi hale <lexi@hale.su>
            3  + *  $ cc -Ofast newtab.c -onewtab \
            4  + *      [-D_default_qutebrowser_location=/...]
            5  + *  $ ./newtab [example.net]
            6  + *  © AGPLv3
            7  + *  ? may god have mercy on my soul.
            8  + *    i wrote this because qutebrowser, being a python
            9  + *    abomination, takes an absurdly fucking long time
           10  + *    to load, even when there's already an instance
           11  + *    running and i just want a new goddamn tab. it
           12  + *    turns out, qutebrowser publishes an inane IPC
           13  + *    mechanism, in which JSON (i know!!) written to a
           14  + *    unix domain socket (I KNOW) can send signals to
           15  + *    a running qutebrowser process. newtab checks if
           16  + *    there's a running qutebrowser process and sends
           17  + *    the appropriate IPC signal if so. otherwise, it
           18  + *    execs a new qutebrowser instance.
           19  + *
           20  + *    it takes a single argument, the URI of a page to
           21  + *    navigate to. if launched without an argument,
           22  + *    newtab simply opens a blank new tab. simple
           23  + *    enough, right? but why write it in C? to talk to
           24  + *    a goddammned python application, of all things?
           25  + *    well, i was originally going to do this in a
           26  + *    bash script with socat, but then i pulled up the
           27  + *    socat manpage and immediately decided it would
           28  + *    be faster to implement the damn thing in C, and
           29  + *    performance would be better anyway. i think it
           30  + *    is fair to say that i was correct.
           31  + *
           32  + *    so, that's half the story. the code quality is
           33  + *    the other half. i'm going to be straight with
           34  + *    you: i wrote this sleep-deprived at 7am because
           35  + *    i'd stayed up all night with my boyfriend who's
           36  + *    still remote-working on German time because his
           37  + *    employers are kind of assholes. i'm aware that's
           38  + *    not an excuse and i apologize sincerely. if
           39  + *    anyone wants to submit a MR to tidy up this
           40  + *    abomination without sacrificing performance, i
           41  + *    would welcome it gratefully. */
           42  +
           43  +#include <stdio.h>
           44  +#include <stdlib.h>
           45  +#include <sys/types.h>
           46  +#include <sys/socket.h>
           47  +#include <sys/un.h>
           48  +#include <dirent.h>
           49  +#include <unistd.h>
           50  +#define _POSIX_C_SOURCE 200809L
           51  +#include <string.h>
           52  +
           53  +#define ssz(str) (str), (sizeof str)
           54  +#define dupl(x) x,x
           55  +
           56  +#ifndef _default_qutebrowser_location
           57  +#	define _default_qutebrowser_location "/usr/bin/qutebrowser"
           58  +#endif
           59  +
           60  +#define jstr(val) "\"" val "\""
           61  +#define j(key, val) jstr(#key) ": " val
           62  +#define json_msg_start "{" \
           63  +			j(protocol_version, "1") "," \
           64  +			j(target_arg, jstr("")) "," \
           65  +			jstr("args")": [\""
           66  +#define json_msg_end "\"]}\n"
           67  +
           68  +enum status {
           69  +	ok,
           70  +	start_instance,
           71  +	fail_xdg_null,
           72  +	fail_find,
           73  +};
           74  +
           75  +const char* errors [] = {
           76  +	[ok] = NULL,
           77  +	[start_instance] = NULL,
           78  +	[fail_xdg_null] = "$XDG_RUNTIME_DIR is not set - cannot locate IPC socket",
           79  +	[fail_find] = "cannot find qutebrowser - is your $PATH set correctly?",
           80  +};
           81  +
           82  +enum status
           83  +transmit(const char* uri) {
           84  +	const char* const run = getenv("XDG_RUNTIME_DIR");
           85  +	if (run == NULL) return fail_xdg_null;
           86  +
           87  +	struct sockaddr_un srv = {
           88  +		.sun_family = AF_UNIX,
           89  +	} ; {
           90  +		/* fuck this fuck this fuck this fuck this */
           91  +		char* end = stpncpy(srv.sun_path, run, sizeof srv.sun_path);
           92  +		end = stpncpy(end, ssz("/qutebrowser/"));
           93  +		DIR* qb = opendir(srv.sun_path);
           94  +		if (!qb) return start_instance;
           95  +		struct dirent* ent;
           96  +		while (ent = readdir(qb)) {
           97  +			if (ent == NULL) return start_instance;
           98  +			if (ent -> d_name[0] != '.') break;
           99  +		}
          100  +		if (ent == NULL) return start_instance;
          101  +		end = stpncpy(end, ent->d_name,
          102  +				(sizeof srv.sun_path) - (end - srv.sun_path));
          103  +		closedir(qb);
          104  +	}
          105  +
          106  +	int sock = socket(AF_UNIX, SOCK_STREAM, 0);
          107  +	if (connect(sock, (struct sockaddr*)&srv, sizeof srv) != 0)
          108  +		return start_instance;
          109  +
          110  +	const char msg_blank [] = json_msg_start json_msg_end;
          111  +	const size_t extra = uri != NULL ? strlen(uri) : 0;
          112  +	char msg_start [(sizeof json_msg_start) + extra];
          113  +
          114  +	const size_t msgsz = sizeof msg_blank + extra - 1;
          115  +	const char*  msg;
          116  +	if (uri == NULL) msg = msg_blank; else {
          117  +		strcpy(msg_start, json_msg_start);
          118  +		strcpy(msg_start + (sizeof json_msg_start) - 1, uri);
          119  +		strcpy(msg_start + (sizeof json_msg_start) - 1 + extra, json_msg_end);
          120  +		msg = msg_start;
          121  +	}
          122  +
          123  +	const char* cur = msg;
          124  +	while(cur < msg + msgsz)
          125  +		cur += write(sock, cur, msgsz - (cur - msg));
          126  +
          127  +	close(sock);
          128  +
          129  +	return ok;
          130  +}
          131  +
          132  +int main(int argc, char** argv) {
          133  +	if (argc > 2) {
          134  +		printf("\x1b[1musage:\x1b[m %s [uri]\n", argv[0]);
          135  +	} else {
          136  +		const char* uri = argc < 2 ? NULL : argv[1];
          137  +		enum status st = transmit(uri);
          138  +		if (st == start_instance) {
          139  +			if (!fork()) {
          140  +				execl(dupl(_default_qutebrowser_location), uri, NULL);
          141  +				execl(dupl("/usr/local/bin/qutebrowser"), uri, NULL);
          142  +				execlp(dupl("qutebrowser"), uri, NULL);
          143  +				st = fail_find;
          144  +			} else {
          145  +				return start_instance;
          146  +			}
          147  +		}
          148  +		if (errors[st] != NULL) printf("\x1b[1;31m(error)\x1b[m %s\n", errors[st]);
          149  +		return st;
          150  +	}
          151  +}