diff --git a/Makefile b/Makefile index 9b9b0d0..931796f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -SRC = atoms.c drw.c dwm.c helpers.c layouts.c settings.c spawn.c tags.c util.c +SRC = atoms.c drw.c dwm.c helpers.c layouts.c menu.c settings.c spawn.c tags.c util.c OBJ = ${SRC:.c=.o} DWM_SRC = dwm/handlers.c dwm/layouts.c dwm/swallow.c dwm/systray.c @@ -21,7 +21,7 @@ options: ${CC} -c $< -o $@ ${CFLAGS} dwm.o: ${DWM_SRC} ${DWM_HDR} -${OBJ}: atoms.h drw.h config.def.h config.mk helpers.h layouts.h settings.h spawn.h tags.h util.h +${OBJ}: atoms.h drw.h config.def.h config.mk helpers.h layouts.h menu.h settings.h spawn.h tags.h util.h polytreewm: ${OBJ} ${CC} -o $@ ${OBJ} ${LDFLAGS} diff --git a/dwm.c b/dwm.c index 49f0cee..65678d4 100644 --- a/dwm.c +++ b/dwm.c @@ -20,7 +20,6 @@ * * To understand everything else, start reading main(). */ -#include #include #include #include @@ -50,6 +49,7 @@ #include "drw.h" #include "helpers.h" #include "layouts.h" +#include "menu.h" #include "settings.h" #include "spawn.h" #include "tags.h" @@ -1243,25 +1243,11 @@ movemouse(const Arg *arg) // TODO: this function really needs to be refactored void nametag(const Arg *arg) { - char *p, name[TAGS_CUSTOM_NAME_SIZE]; - FILE *f; - int i; + char name[TAGS_CUSTOM_NAME_SIZE]; - errno = 0; // popen(3p) says on failure it "may" set errno - if(!(f = popen("rofi -dmenu -p \"Tag name\"", "r"))) { - fprintf(stderr, "polytreewm: popen 'rofi -dmenu -p \"Tag name\"' failed%s%s\n", errno ? ": " : "", errno ? strerror(errno) : ""); - return; - } - if (!(p = fgets(name, TAGS_CUSTOM_NAME_SIZE, f)) && (i = errno) && ferror(f)) - fprintf(stderr, "polytreewm: fgets failed: %s\n", strerror(i)); - if (pclose(f) < 0) - fprintf(stderr, "polytreewm: pclose failed: %s\n", strerror(errno)); - if(!p) - return; - if((p = strchr(name, '\n'))) - *p = '\0'; + if (!menu_run(name, TAGS_CUSTOM_NAME_SIZE, "Tag name")) return; - for (i = 0; i < TAGS_COUNT; ++i) { + for (int i = 0; i < TAGS_COUNT; ++i) { if (selmon->tagset[selmon->seltags] & (1 << i)) { tags_rename(i, name); } diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..c7804df --- /dev/null +++ b/menu.c @@ -0,0 +1,73 @@ +#include "menu.h" + +#include +#include +#include + +#define COMMAND_SIZE 256 + +bool menu_run( + char *const buffer, + const size_t buffer_size, + const char *prompt +) { + // TODO: maybe we should assert here + if (buffer == NULL || buffer_size < 2) return false; + + if (prompt == NULL) { + prompt = ""; + } + + for (const char *s = prompt; *s; ++s) { + // Simple check to ensure that the prompt + // string doesn't need to be escaped. + // TODO: maybe we should assert here + if (*s == '"') return false; + } + + char command[COMMAND_SIZE]; + const int command_slen = + snprintf(command, COMMAND_SIZE, "rofi -dmenu -p \"%s\"", prompt); + + // Simple check to ensure that the prompt string is not too long. + // TODO: maybe we should assert here + if (command_slen >= COMMAND_SIZE) return false; + + errno = 0; // popen(3p) says on failure it "may" set errno + FILE *f = popen(command, "r"); + + if (!f) { + perror("polytreewm: menu: popen failed"); + return false; + } + + const char *const buffer_result = fgets(buffer, buffer_size, f); + + // Just to ensure that string is null-terminated. + // The man page fgets(3) is not very clear about that. + buffer[buffer_size - 1] = '\0'; + + if (!buffer_result) { + perror("polytreewm: menu: fgets failed"); + pclose(f); + return false; + } + + // Fail on both pipe error and not successfull exit status. + if (pclose(f) != EXIT_SUCCESS) { + perror("polytreewm: menu: pclose failed"); + return false; + } + + for (char *s = buffer; *s; ++s) { + if (*s == '\n') { + if (*(s + 1) == '\0') { + *s = '\0'; + } else { + *s = ' '; + } + } + } + + return true; +} diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..af2f5fd --- /dev/null +++ b/menu.h @@ -0,0 +1,9 @@ +#ifndef _MENU_H +#define _MENU_H + +#include +#include + +bool menu_run(char *buffer, size_t buffer_size, const char *prompt); + +#endif // _MENU_H