#define _GNU_SOURCE /* program_invocation_name */ #include /* assert(3p) */ #include /* program_invocation_name */ #include /* error(3gnu) */ #include /* O_* flags to open(3p) */ #include /* glob(3p), globfree(3p), GLOB_* */ #include /* sigaction(3p) */ #include /* bool */ #include /* dprintf(3p) */ #include /* exit(3p), malloc(3p) */ #include /* strcmp(3p), strlen(3p), strcpy(3p) */ #include /* open(3p) */ #include /* unlink(3p) */ char *rbar_globescape(const char *lit); /* Main *************************************************************/ struct impl { int (*left_click)(void); int (*middle_click)(void); int (*right_click)(void); int (*scroll_up)(void); int (*scroll_down)(void); int (*update)(const char *tag); } impl; #define errusage(...) do { \ error(6, 0, __VA_ARGS__); \ usage(2); \ exit(2); \ } while(0) void usage(int fd) { dprintf(fd, "Usage: %1$s NN_LABEL\n" " or: %1$s BTN_ID\n" " or: %1$s -h\n", program_invocation_name); } void sigexit(int sig) { error(0, 0, "Caught signal: %d", sig); exit(0); } char *xrd_glob; size_t xrd_glob_len; void xrd_cleanup(void) { free(xrd_glob); } void xrd_setup(void) { char *xrd = getenv("XDG_RUNTIME_DIR"); if (xrd == NULL || xrd[0] == '\0') { error(6, 0, "XDG_RUNTIME_DIR isn't set"); } xrd_glob = rbar_globescape(xrd); xrd_glob_len = strlen(xrd_glob); atexit(xrd_cleanup); } int main(int argc, char *argv[]) { if (argc != 2) { errusage("exactly 1 argument expected, got %d", argc-1); } const char *arg = argv[1]; if (strcmp(arg, "-h") == 0) { usage(1); exit(0); } struct sigaction sa = { 0 }; sa.sa_handler = sigexit; sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); if ('1' <= arg[0] && arg[0] <= '5' && arg[1] == '\0') { int (*fn)(void) = NULL; switch (arg[0]) { case '1': fn = impl.left_click; break; case '2': fn = impl.middle_click; break; case '3': fn = impl.right_click; break; case '4': fn = impl.scroll_up; break; case '5': fn = impl.scroll_down; break; } xrd_setup(); if (fn == NULL) { exit(0); } else { exit(fn()); } } if ('0' <= arg[0] && arg[0] <= '9' && '0' <= arg[1] && arg[1] <= '9' && arg[2] == '_') { xrd_setup(); if (impl.update == NULL) { exit(0); } else { exit(impl.update(arg)); } } errusage("invalid argument: %s", arg); } /* Util *************************************************************/ #define RBAR_DIRGLOB "/wmii*/rbar" char *rbar_globescape(const char *lit) { size_t len = 0; for (const char *a = lit; *a != '\0'; a++) { switch (*a) { case '\\': case '*': case '?': case '[': len++; /* fall through */ default: len++; } } char *glob = malloc(len+1); if (!glob) error(1, errno, "rbar_globescape: malloc"); // copy 'a' to 'b' char *b = glob; for (const char *a = lit; *a != '\0'; a++) { switch (*a) { case '\\': case '*': case '?': case '[': *(b++) = '\\'; /* fall through */ default: *(b++) = *a; } } *b = '\0'; return glob; } /* returns 0 or GLOB_NOMATCH */ bool rbar_write(const char *filename, const char *msg) { char *fullglob = alloca(xrd_glob_len+(sizeof RBAR_DIRGLOB)); strcpy(fullglob, xrd_glob); strcpy(&fullglob[xrd_glob_len], RBAR_DIRGLOB); glob_t __attribute__((__cleanup__(globfree))) globbuf = { 0 }; switch (glob(fullglob, GLOB_ONLYDIR|GLOB_NOSORT|GLOB_ERR, NULL, &globbuf)) { case GLOB_NOSPACE: error(1, ENOMEM, "rbar_write"); assert(0); case GLOB_ABORTED: error(1, errno, "rbar_write: glob"); assert(0); case GLOB_NOMATCH: return false; } const size_t filenamelen = strlen(filename); const size_t msglen = strlen(msg); for (size_t i = 0; i < globbuf.gl_pathc; i++) { const char *dirname = globbuf.gl_pathv[i]; const size_t dirnamelen = strlen(dirname); char *fullname = alloca(dirnamelen+1+filenamelen+1); strcpy(fullname, dirname); fullname[dirnamelen] = '/'; strcpy(&fullname[dirnamelen+1], filename); int fd = open(fullname, O_WRONLY|O_APPEND|O_CREAT, 0666); if (fd < 0) { error(0, errno, "open: %s", fullname); continue; } if (write(fd, msg, msglen) < (ssize_t)msglen) error(0, errno, "write: %s", fullname); close(fd); } return true; } void rbar_remove(const char *fileglob) { char *fullglob = alloca(xrd_glob_len+(sizeof RBAR_DIRGLOB)+strlen(fileglob)+1); strcpy(fullglob, xrd_glob); strcpy(&fullglob[xrd_glob_len], RBAR_DIRGLOB); fullglob[xrd_glob_len+(sizeof RBAR_DIRGLOB)-1] = '/'; strcpy(&fullglob[xrd_glob_len+(sizeof RBAR_DIRGLOB)], fileglob); glob_t __attribute__((__cleanup__(globfree))) globbuf = { 0 }; if (glob(fullglob, GLOB_NOSORT, NULL, &globbuf) != 0) return; for (size_t i = 0; i < globbuf.gl_pathc; i++) { unlink(globbuf.gl_pathv[i]); } }