diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6af4e3e..b7cc1d4 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,6 +1,10 @@ # Sources -SET(CLIENT_SOURCE client.c) -SET(CLIENT_INCLUDE ${BEMENU_INCLUDE_DIRS}) +SET(CLIENT_SOURCES + bemenu.c + bemenu-run.c +) + +SET(CLIENT_INCLUDES ${BEMENU_INCLUDE_DIRS} "common") SET(CLIENT_LIBRARIES ${BEMENU_LIBRARIES}) # Warnings @@ -17,12 +21,13 @@ IF (UNIX AND CMAKE_COMPILER_IS_GNUCC) ENDIF () # Compile -INCLUDE_DIRECTORIES(${CLIENT_INCLUDE}) -ADD_EXECUTABLE(client ${CLIENT_SOURCE}) -TARGET_LINK_LIBRARIES(client ${CLIENT_LIBRARIES}) -SET_TARGET_PROPERTIES(client PROPERTIES OUTPUT_NAME bemenu) - -# Install -INSTALL(TARGETS client DESTINATION bin) +FOREACH (SOURCE ${CLIENT_SOURCES}) + INCLUDE_DIRECTORIES(${CLIENT_INCLUDES}) + ADD_EXECUTABLE(${SOURCE} ${SOURCE} common/common.c) + TARGET_LINK_LIBRARIES(${SOURCE} ${CLIENT_LIBRARIES}) + get_filename_component(ONAME ${SOURCE} NAME_WE) + SET_TARGET_PROPERTIES(${SOURCE} PROPERTIES OUTPUT_NAME ${ONAME}) + INSTALL(TARGETS ${SOURCE} DESTINATION bin) +ENDFOREACH () # vim: set ts=8 sw=4 tw=0 : diff --git a/client/bemenu-run.c b/client/bemenu-run.c new file mode 100644 index 0000000..6c7228d --- /dev/null +++ b/client/bemenu-run.c @@ -0,0 +1,184 @@ +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "../lib/3rdparty/tinydir.h" + +static struct client client = { + .prioritory = BM_PRIO_ANY, + .filter_mode = BM_FILTER_MODE_DMENU, + .wrap = 0, + .lines = 0, + .colors = {0}, + .title = "bemenu", + .renderer = NULL, + .font = NULL, + .font_size = 0, + .selected = 0, + .bottom = 0, + .grab = 0, + .monitor = 0 +}; + +struct paths { + char *path; + char *paths; +}; + +static char* +c_strdup(const char *str) +{ + size_t size = strlen(str); + char *cpy = calloc(1, size + 1); + return (cpy ? memcpy(cpy, str, size) : NULL); +} + + static char* +strip_slash(char *str) +{ + size_t size = strlen(str); + if (size > 0) + for (char *s = str + size - 1; *s == '/'; --s) + *s = 0; + return str; +} + +static const char* +get_paths(const char *env, const char *default_paths, struct paths *state) +{ + if (state->path && !*state->path) { + free(state->paths); + return NULL; + } + + if (!state->paths) { + const char *paths; + if (!(paths = getenv(env)) || !paths[0]) + paths = default_paths; + + state->path = state->paths = c_strdup(paths); + } + + if (!state->path || !state->paths) + return NULL; + + char *path; + do { + size_t f; + path = state->path; + if ((f = strcspn(state->path, ":")) > 0) { + state->path += f + (path[f] ? 1 : 0); + path[f] = 0; + } + + if (!*path) { + free(state->paths); + return NULL; + } + } while (path[0] != '/'); + + return strip_slash(path); +} + +static void +read_items_to_menu_from_dir(struct bm_menu *menu, const char *path) +{ + assert(menu && path); + + tinydir_dir dir; + if (tinydir_open(&dir, path) == -1) + return; + + while (dir.has_next) { + tinydir_file file; + memset(&file, 0, sizeof(file)); + tinydir_readfile(&dir, &file); + + if (!file.is_dir) { + struct bm_item *item; + if (!(item = bm_item_new(file.name))) + break; + + bm_menu_add_item(menu, item); + } + + tinydir_next(&dir); + } + + tinydir_close(&dir); +} + +static void +read_items_to_menu_from_path(struct bm_menu *menu) +{ + assert(menu); + + const char *path; + struct paths state; + memset(&state, 0, sizeof(state)); + while ((path = get_paths("PATH", "/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/bin:/sbin", &state))) + read_items_to_menu_from_dir(menu, path); +} + +static void +launch(const char *bin) +{ + if (!bin) + return; + + if (fork() == 0) { + setsid(); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + execlp(bin, bin, NULL); + _exit(EXIT_SUCCESS); + } +} + +int +main(int argc, char **argv) +{ + struct sigaction action = { + .sa_handler = SIG_DFL, + .sa_flags = SA_NOCLDWAIT + }; + + // do not care about childs + sigaction(SIGCHLD, &action, NULL); + + if (!bm_init()) + return EXIT_FAILURE; + + parse_args(&client, &argc, &argv); + + struct bm_menu *menu; + if (!(menu = menu_with_options(&client))) + return EXIT_FAILURE; + + read_items_to_menu_from_path(menu); + bm_menu_set_highlighted_index(menu, client.selected); + + enum bm_run_result status = run_menu(menu); + + if (status == BM_RUN_RESULT_SELECTED) { + uint32_t i, count; + struct bm_item **items = bm_menu_get_selected_items(menu, &count); + for (i = 0; i < count; ++i) { + const char *text = bm_item_get_text(items[i]); + launch(text); + } + + if (!count && bm_menu_get_filter(menu)) + launch(bm_menu_get_filter(menu)); + } + + free(client.font); + bm_menu_free(menu); + return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/client/bemenu.c b/client/bemenu.c new file mode 100644 index 0000000..96ad72e --- /dev/null +++ b/client/bemenu.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include "common.h" + +static struct client client = { + .prioritory = BM_PRIO_ANY, + .filter_mode = BM_FILTER_MODE_DMENU, + .wrap = 0, + .lines = 0, + .colors = {0}, + .title = "bemenu", + .renderer = NULL, + .font = NULL, + .font_size = 0, + .selected = 0, + .bottom = 0, + .grab = 0, + .monitor = 0 +}; + +static void +read_items_to_menu_from_stdin(struct bm_menu *menu) +{ + assert(menu); + + size_t step = 1024, allocated; + char *buffer; + + if (!(buffer = malloc((allocated = step)))) + return; + + size_t read; + while ((read = fread(buffer + (allocated - step), 1, step, stdin)) == step) { + void *tmp; + if (!(tmp = realloc(buffer, (allocated += step)))) { + free(buffer); + return; + } + buffer = tmp; + } + buffer[allocated - step + read - 1] = 0; + + char *s = buffer; + while ((size_t)(s - buffer) < allocated - step + read) { + size_t pos = strcspn(s, "\n"); + if (pos == 0) { + s += 1; + continue; + } + + s[pos] = 0; + + struct bm_item *item; + if (!(item = bm_item_new(s))) + break; + + bm_menu_add_item(menu, item); + s += pos + 1; + } + + free(buffer); +} + +int +main(int argc, char **argv) +{ + if (!bm_init()) + return EXIT_FAILURE; + + parse_args(&client, &argc, &argv); + + struct bm_menu *menu; + if (!(menu = menu_with_options(&client))) + return EXIT_FAILURE; + + read_items_to_menu_from_stdin(menu); + bm_menu_set_highlighted_index(menu, client.selected); + + enum bm_run_result status = run_menu(menu); + + if (status == BM_RUN_RESULT_SELECTED) { + uint32_t i, count; + struct bm_item **items = bm_menu_get_selected_items(menu, &count); + for (i = 0; i < count; ++i) { + const char *text = bm_item_get_text(items[i]); + printf("%s\n", (text ? text : "")); + } + + if (!count && bm_menu_get_filter(menu)) + printf("%s\n", bm_menu_get_filter(menu)); + } + + free(client.font); + bm_menu_free(menu); + return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/client/client.c b/client/common/common.c similarity index 64% rename from client/client.c rename to client/common/common.c index 52933d1..e34a34f 100644 --- a/client/client.c +++ b/client/common/common.c @@ -1,42 +1,12 @@ #define _DEFAULT_SOURCE +#include "common.h" #include -#include #include -#include -#include #include +#include +#include #include -#include - -static struct { - enum bm_prioritory prioritory; - enum bm_filter_mode filter_mode; - int32_t wrap; - uint32_t lines; - const char *colors[BM_COLOR_LAST]; - const char *title; - const char *renderer; - char *font; - uint32_t font_size; - int32_t selected; - int32_t bottom; - int32_t grab; - int32_t monitor; -} client = { - .prioritory = BM_PRIO_ANY, - .filter_mode = BM_FILTER_MODE_DMENU, - .wrap = 0, - .lines = 0, - .colors = {0}, - .title = "bemenu", - .renderer = NULL, - .font = NULL, - .font_size = 0, - .selected = 0, - .bottom = 0, - .grab = 0, - .monitor = 0 -}; +#include static void disco_trap(int sig) @@ -77,6 +47,7 @@ disco(void) static void version(const char *name) { + assert(name); char *base = strrchr(name, '/'); printf("%s v%s\n", (base ? base + 1 : name), bm_version()); exit(EXIT_SUCCESS); @@ -85,6 +56,8 @@ version(const char *name) static void usage(FILE *out, const char *name) { + assert(out && name); + char *base = strrchr(name, '/'); fprintf(out, "usage: %s [options]\n", (base ? base + 1 : name)); fputs("Options\n" @@ -117,12 +90,15 @@ usage(FILE *out, const char *name) " --hf defines the highlighted foreground color. (w)\n" " --sb defines the selected background color. (w)\n" " --sf defines the selected foreground color. (w)\n", out); + exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS)); } -static void -parse_args(int *argc, char **argv[]) +void +parse_args(struct client *client, int *argc, char **argv[]) { + assert(client && argc && argv); + static const struct option opts[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, @@ -173,78 +149,78 @@ parse_args(int *argc, char **argv[]) break; case 'i': - client.filter_mode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE; + client->filter_mode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE; break; case 'w': - client.wrap = 1; + client->wrap = 1; break; case 'l': - client.lines = strtol(optarg, NULL, 10); + client->lines = strtol(optarg, NULL, 10); break; case 'p': - client.title = optarg; + client->title = optarg; break; case 'I': - client.selected = strtol(optarg, NULL, 10); + client->selected = strtol(optarg, NULL, 10); break; case 0x100: - client.renderer = optarg; + client->renderer = optarg; break; case 0x101: if (!strcmp(optarg, "terminal")) - client.prioritory = BM_PRIO_TERMINAL; + client->prioritory = BM_PRIO_TERMINAL; else if (!strcmp(optarg, "gui")) - client.prioritory = BM_PRIO_GUI; + client->prioritory = BM_PRIO_GUI; break; case 'b': - client.bottom = 1; + client->bottom = 1; break; case 'f': - client.grab = 1; + client->grab = 1; break; case 'm': - client.monitor = strtol(optarg, NULL, 10); + client->monitor = strtol(optarg, NULL, 10); break; case 0x102: - if (sscanf(optarg, "%ms:%u", &client.font, &client.font_size) < 2) - sscanf(optarg, "%ms", &client.font); + if (sscanf(optarg, "%ms:%u", &client->font, &client->font_size) < 2) + sscanf(optarg, "%ms", &client->font); break; case 0x103: - client.colors[BM_COLOR_BG] = optarg; + client->colors[BM_COLOR_BG] = optarg; break; case 0x104: - client.colors[BM_COLOR_TITLE_BG] = optarg; + client->colors[BM_COLOR_TITLE_BG] = optarg; break; case 0x105: - client.colors[BM_COLOR_TITLE_FG] = optarg; + client->colors[BM_COLOR_TITLE_FG] = optarg; break; case 0x106: - client.colors[BM_COLOR_FILTER_BG] = optarg; + client->colors[BM_COLOR_FILTER_BG] = optarg; break; case 0x107: - client.colors[BM_COLOR_FILTER_FG] = optarg; + client->colors[BM_COLOR_FILTER_FG] = optarg; break; case 0x108: - client.colors[BM_COLOR_ITEM_BG] = optarg; + client->colors[BM_COLOR_ITEM_BG] = optarg; break; case 0x109: - client.colors[BM_COLOR_ITEM_FG] = optarg; + client->colors[BM_COLOR_ITEM_FG] = optarg; break; case 0x110: - client.colors[BM_COLOR_HIGHLIGHTED_BG] = optarg; + client->colors[BM_COLOR_HIGHLIGHTED_BG] = optarg; break; case 0x111: - client.colors[BM_COLOR_HIGHLIGHTED_FG] = optarg; + client->colors[BM_COLOR_HIGHLIGHTED_FG] = optarg; break; case 0x112: - client.colors[BM_COLOR_SELECTED_BG] = optarg; + client->colors[BM_COLOR_SELECTED_BG] = optarg; break; case 0x113: - client.colors[BM_COLOR_SELECTED_FG] = optarg; + client->colors[BM_COLOR_SELECTED_FG] = optarg; break; case 0x114: @@ -263,97 +239,36 @@ parse_args(int *argc, char **argv[]) *argv += optind; } -static void -read_items_to_menu_from_stdin(struct bm_menu *menu) +struct bm_menu* +menu_with_options(struct client *client) { - assert(menu); - - size_t step = 1024, allocated; - char *buffer; - - if (!(buffer = malloc((allocated = step)))) - return; - - size_t read; - while ((read = fread(buffer + (allocated - step), 1, step, stdin)) == step) { - void *tmp; - if (!(tmp = realloc(buffer, (allocated += step)))) { - free(buffer); - return; - } - buffer = tmp; - } - buffer[allocated - step + read - 1] = 0; - - char *s = buffer; - while ((size_t)(s - buffer) < allocated - step + read) { - size_t pos = strcspn(s, "\n"); - if (pos == 0) { - s += 1; - continue; - } - - s[pos] = 0; - - struct bm_item *item; - if (!(item = bm_item_new(s))) - break; - - bm_menu_add_item(menu, item); - s += pos + 1; - } - - free(buffer); -} - -int -main(int argc, char **argv) -{ - if (!bm_init()) - return EXIT_FAILURE; - - parse_args(&argc, &argv); - struct bm_menu *menu; - if (!(menu = bm_menu_new(client.renderer, client.prioritory))) - return EXIT_FAILURE; + if (!(menu = bm_menu_new(client->renderer, client->prioritory))) + return NULL; - bm_menu_set_font(menu, client.font, client.font_size); - bm_menu_set_title(menu, client.title); - bm_menu_set_filter_mode(menu, client.filter_mode); - bm_menu_set_lines(menu, client.lines); - bm_menu_set_wrap(menu, client.wrap); + bm_menu_set_font(menu, client->font, client->font_size); + bm_menu_set_title(menu, client->title); + bm_menu_set_filter_mode(menu, client->filter_mode); + bm_menu_set_lines(menu, client->lines); + bm_menu_set_wrap(menu, client->wrap); for (uint32_t i = 0; i < BM_COLOR_LAST; ++i) - bm_menu_set_color(menu, i, client.colors[i]); + bm_menu_set_color(menu, i, client->colors[i]); - read_items_to_menu_from_stdin(menu); + return menu; +} - bm_menu_set_highlighted_index(menu, client.selected); - - enum bm_key key; +enum bm_run_result +run_menu(struct bm_menu *menu) +{ uint32_t unicode; - int32_t status = 0; + enum bm_key key; + enum bm_run_result status = BM_RUN_RESULT_RUNNING; do { bm_menu_render(menu); key = bm_menu_poll_key(menu, &unicode); } while ((status = bm_menu_run_with_key(menu, key, unicode)) == BM_RUN_RESULT_RUNNING); - - if (status == BM_RUN_RESULT_SELECTED) { - uint32_t i, count; - struct bm_item **items = bm_menu_get_selected_items(menu, &count); - for (i = 0; i < count; ++i) { - const char *text = bm_item_get_text(items[i]); - printf("%s\n", (text ? text : "")); - } - - if (!count && bm_menu_get_filter(menu)) - printf("%s\n", bm_menu_get_filter(menu)); - } - - free(client.font); - bm_menu_free(menu); - return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); + return status; } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/client/common/common.h b/client/common/common.h new file mode 100644 index 0000000..b87b974 --- /dev/null +++ b/client/common/common.h @@ -0,0 +1,28 @@ +#ifndef _BM_COMMON_H_ +#define _BM_COMMON_H_ + +#include + +struct client { + enum bm_prioritory prioritory; + enum bm_filter_mode filter_mode; + int32_t wrap; + uint32_t lines; + const char *colors[BM_COLOR_LAST]; + const char *title; + const char *renderer; + char *font; + uint32_t font_size; + int32_t selected; + int32_t bottom; + int32_t grab; + int32_t monitor; +}; + +void parse_args(struct client *client, int *argc, char **argv[]); +struct bm_menu* menu_with_options(struct client *client); +enum bm_run_result run_menu(struct bm_menu *menu); + +#endif /* _BM_COMMON_H_ */ + +/* vim: set ts=8 sw=4 tw=0 :*/