/* [ʞ] newtab.c * ~ lexi hale * $ cc -Ofast newtab.c -onewtab \ * [-D_default_qutebrowser_location=/...] \ * [-D_enable_vblank] * $ ./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 #include #include #include #include #include #include #define _POSIX_C_SOURCE 200809L #include #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()) { # ifndef _enable_vblank setenv("vblank_mode","0",0); # endif 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; } }