diff --git a/README.md b/README.md index 4c2754a..06732a7 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ All dependencies are searched with `pkg-config` | Variable | Description | Value | |------------------|-----------------------------------------|----------------------| +| BEMENU_OPTS | Options for bemenu, bemenu-run from env | Any cli argument | | BEMENU_BACKEND | Forces a renderer by name | x11, wayland, curses | | BEMENU_RENDERER | Forces a renderer by loading a .so file | Path to the .so file | | BEMENU_RENDERERS | Override renderer load directory | Path to a directory | diff --git a/client/bemenu-run.c b/client/bemenu-run.c index 618aac6..602dd6e 100644 --- a/client/bemenu-run.c +++ b/client/bemenu-run.c @@ -17,17 +17,10 @@ struct paths { char *paths; }; -static char* -c_strdup2(const char *str, size_t size) -{ - char *cpy = calloc(1, size + 1); - return (cpy ? memcpy(cpy, str, size) : NULL); -} - static char* c_strdup(const char *str) { - return c_strdup2(str, strlen(str)); + return cstrcopy(str, strlen(str)); } static char* @@ -134,73 +127,6 @@ read_items_to_menu_from_path(struct bm_menu *menu) read_items_to_menu_from_dir(menu, path); } -#define WHITESPACE " \t\n\r" - -static const char* -tokenize(const char *cstr, size_t *out_len, const char *separator, bool skip_whitespace, const char **state) -{ - assert(out_len && separator && state); - const char *current = (state && *state ? *state : cstr); - - if (!current || !*current || !cstr || !*cstr) - return NULL; - - current += strspn(current, separator); - - if (skip_whitespace) - current += strspn(current, WHITESPACE); - - *out_len = strcspn(current, separator); - *state = current + *out_len; - - if (skip_whitespace) { - const size_t ws = strcspn(current, WHITESPACE); - *out_len -= (ws < *out_len ? *out_len - ws : 0); - } - - return current; -} - -static const char* -tokenize_quoted(const char *cstr, size_t *out_len, const char *separator, const char *quotes, const char **state) -{ - assert(out_len && separator && quotes && state); - const char *e, *current = tokenize(cstr, out_len, separator, true, state); - - if (!current) - return NULL; - - for (const char *q = quotes; *q; ++q) { - if (*current != *q) - continue; - - bool escaped = false; - for (e = ++current; *e; ++e) { - if (escaped) - escaped = false; - else if (*e == '\\') - escaped = true; - else if (*e == *q) - break; - } - - *out_len = e - current; - e = (!*e ? e : e + 1); - - if (*e) { - size_t tmp; - const char *state2 = NULL; - *state = tokenize(e, &tmp, separator, true, &state2); - } else { - *state = e; - } - - break; - } - - return current; -} - static inline void ignore_ret(int useless, ...) { (void)useless; } static void @@ -214,27 +140,10 @@ launch(const char *bin) ignore_ret(0, freopen("/dev/null", "w", stdout)); ignore_ret(0, freopen("/dev/null", "w", stderr)); - size_t count = 0; - { - size_t len; - const char *state = NULL; - while (tokenize_quoted(bin, &len, " ", "\"'", &state)) - ++count; - } - char **tokens; - if (!count || !(tokens = calloc(count + 1, sizeof(char*)))) + if (!(tokens = tokenize_quoted_to_argv(bin, NULL, NULL))) _exit(EXIT_FAILURE); - { - size_t i = 0, len; - const char *t, *state = NULL; - while (i < count && (t = tokenize_quoted(bin, &len, " ", "\"'", &state))) { - if (!(tokens[i++] = c_strdup2(t, len))) - _exit(EXIT_FAILURE); - } - } - execvp(tokens[0], tokens); _exit(EXIT_SUCCESS); } diff --git a/client/common/common.c b/client/common/common.c index 4d6b47f..f36fdf1 100644 --- a/client/common/common.c +++ b/client/common/common.c @@ -43,6 +43,111 @@ disco(void) exit(EXIT_SUCCESS); } +#define WHITESPACE " \t\n\r" + +static const char* +tokenize(const char *cstr, size_t *out_len, const char *separator, bool skip_whitespace, const char **state) +{ + assert(out_len && separator && state); + const char *current = (state && *state ? *state : cstr); + + if (!current || !*current || !cstr || !*cstr) + return NULL; + + current += strspn(current, separator); + + if (skip_whitespace) + current += strspn(current, WHITESPACE); + + *out_len = strcspn(current, separator); + *state = current + *out_len; + + if (skip_whitespace) { + const size_t ws = strcspn(current, WHITESPACE); + *out_len -= (ws < *out_len ? *out_len - ws : 0); + } + + return current; +} + +static const char* +tokenize_quoted(const char *cstr, size_t *out_len, const char *separator, const char *quotes, const char **state) +{ + assert(out_len && separator && quotes && state); + const char *e, *current = tokenize(cstr, out_len, separator, true, state); + + if (!current) + return NULL; + + for (const char *q = quotes; *q; ++q) { + if (*current != *q) + continue; + + bool escaped = false; + for (e = ++current; *e; ++e) { + if (escaped) + escaped = false; + else if (*e == '\\') + escaped = true; + else if (*e == *q) + break; + } + + *out_len = e - current; + e = (!*e ? e : e + 1); + + if (*e) { + size_t tmp; + const char *state2 = NULL; + *state = tokenize(e, &tmp, separator, true, &state2); + } else { + *state = e; + } + + break; + } + + return current; +} + +char* +cstrcopy(const char *str, size_t size) +{ + char *cpy = calloc(1, size + 1); + return (cpy ? memcpy(cpy, str, size) : NULL); +} + +char** +tokenize_quoted_to_argv(const char *str, char *argv0, int *out_argc) +{ + if (out_argc) *out_argc = 0; + + size_t count = !!argv0; + { + size_t len; + const char *state = NULL; + while (tokenize_quoted(str, &len, " ", "\"'", &state)) + ++count; + } + + char **tokens; + if (!count || !(tokens = calloc(count + 1, sizeof(char*)))) + return NULL; + + { + tokens[0] = argv0; + size_t i = !!argv0, len; + const char *t, *state = NULL; + while (i < count && (t = tokenize_quoted(str, &len, " ", "\"'", &state))) { + if (!(tokens[i++] = cstrcopy(t, len))) + return NULL; + } + } + + if (out_argc) *out_argc = count; + return tokens; +} + static void version(const char *name) { @@ -104,8 +209,8 @@ usage(FILE *out, const char *name) exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS)); } -void -parse_args(struct client *client, int *argc, char **argv[]) +static void +do_getopt(struct client *client, int *argc, char **argv[]) { assert(client && argc && argv); @@ -149,9 +254,9 @@ parse_args(struct client *client, int *argc, char **argv[]) * Either break the interface and make them --sf, --sb (like they are now), * or parse them before running getopt.. */ - for (;;) { - int32_t opt = getopt_long(*argc, *argv, "hviwl:I:p:P:I:bfm:H:n", opts, NULL); - if (opt < 0) + for (optind = 0;;) { + int32_t opt; + if ((opt = getopt_long(*argc, *argv, "hviwl:I:p:P:I:bfm:H:n", opts, NULL)) < 0) break; switch (opt) { @@ -259,6 +364,17 @@ parse_args(struct client *client, int *argc, char **argv[]) *argv += optind; } +void +parse_args(struct client *client, int *argc, char **argv[]) +{ + int num_opts; + char **opts; + const char *env; + if ((env = getenv("BEMENU_OPTS")) && (opts = tokenize_quoted_to_argv(env, (*argv)[0], &num_opts))) + do_getopt(client, &num_opts, &opts); + do_getopt(client, argc, argv); +} + struct bm_menu* menu_with_options(const struct client *client) { diff --git a/client/common/common.h b/client/common/common.h index ea034b9..cd5526e 100644 --- a/client/common/common.h +++ b/client/common/common.h @@ -2,6 +2,7 @@ #define _BM_COMMON_H_ #include +#include struct client { enum bm_filter_mode filter_mode; @@ -21,6 +22,8 @@ struct client { bool no_overlap; }; +char* cstrcopy(const char *str, size_t size); +char** tokenize_quoted_to_argv(const char *str, char *argv0, int *out_argc); void parse_args(struct client *client, int *argc, char **argv[]); struct bm_menu* menu_with_options(const struct client *client); enum bm_run_result run_menu(const struct client *client, struct bm_menu *menu, void (*item_cb)(struct bm_item *item, const char *text));