forked from RepoMirrors/bemenu
clients: add BEMENU_OPTS env var support
It's possible to pass any CLI argument through BEMENU_OPTS env variable instead.
This commit is contained in:
parent
bc584cc5f0
commit
dac1ffde7e
@ -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 |
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _BM_COMMON_H_
|
||||
|
||||
#include <bemenu.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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));
|
||||
|
Loading…
Reference in New Issue
Block a user