Overview
Comment: | add newtab |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
6b40270f2843f2cbfc588dc51f60abcd |
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 +}