config: add new function config_clone()

This commit is contained in:
Daniel Eklöf 2021-06-18 16:18:41 +02:00
parent 42ec264075
commit f7860aec76
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
6 changed files with 217 additions and 109 deletions

295
config.c
View file

@ -501,40 +501,50 @@ str_to_pt_or_px(const char *s, struct pt_or_px *res, struct config *conf,
return true; return true;
} }
static void NOINLINE static void NOINLINE
free_spawn_template(struct config_spawn_template *template) free_argv(struct argv *argv)
{ {
if (template->argv == NULL) if (argv->args == NULL)
return; return;
for (char **a = argv->args; *a != NULL; a++)
for (char **argv = template->argv; *argv != NULL; argv++) free(*a);
free(*argv); free(argv->args);
free(template->argv); argv->args = NULL;
template->argv = NULL;
} }
static void NOINLINE static void NOINLINE
clone_spawn_template(struct config_spawn_template *dst, clone_argv(struct argv *dst, const struct argv *src)
const struct config_spawn_template *src)
{ {
if (src->argv == NULL) { if (src->args == NULL) {
dst->argv = NULL; dst->args = NULL;
return; return;
} }
size_t count = 0; size_t count = 0;
for (char **argv = src->argv; *argv != NULL; argv++) for (char **args = src->args; *args != NULL; args++)
count++; count++;
dst->argv = xmalloc((count + 1) * sizeof(dst->argv[0])); dst->args = xmalloc((count + 1) * sizeof(dst->args[0]));
for (char **argv_src = src->argv, **argv_dst = dst->argv; for (char **args_src = src->args, **args_dst = dst->args;
*argv_src != NULL; argv_src++, *args_src != NULL; args_src++,
argv_dst++) args_dst++)
{ {
*argv_dst = xstrdup(*argv_src); *args_dst = xstrdup(*args_src);
} }
dst->argv[count] = NULL; dst->args[count] = NULL;
}
static void
spawn_template_free(struct config_spawn_template *template)
{
free_argv(&template->argv);
}
static void
spawn_template_clone(struct config_spawn_template *dst,
const struct config_spawn_template *src)
{
clone_argv(&dst->argv, &src->argv);
} }
static bool NOINLINE static bool NOINLINE
@ -543,7 +553,7 @@ str_to_spawn_template(struct config *conf,
const char *path, int lineno, const char *section, const char *path, int lineno, const char *section,
const char *key) const char *key)
{ {
free_spawn_template(template); spawn_template_free(template);
char **argv = NULL; char **argv = NULL;
@ -554,7 +564,7 @@ str_to_spawn_template(struct config *conf,
return false; return false;
} }
template->argv = argv; template->argv.args = argv;
return true; return true;
} }
@ -1555,12 +1565,12 @@ has_key_binding_collisions(struct config *conf,
bool sym = combo1->sym == combo2->sym; bool sym = combo1->sym == combo2->sym;
if (shift && alt && ctrl && meta && sym) { if (shift && alt && ctrl && meta && sym) {
bool has_pipe = combo1->pipe.cmd != NULL; bool has_pipe = combo1->pipe.argv.args != NULL;
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'", LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'",
path, lineno, combo2->text, path, lineno, combo2->text,
action_map[combo1->action], action_map[combo1->action],
has_pipe ? " [" : "", has_pipe ? " [" : "",
has_pipe ? combo1->pipe.cmd : "", has_pipe ? combo1->pipe.argv.args[0] : "",
has_pipe ? "]" : ""); has_pipe ? "]" : "");
return true; return true;
} }
@ -1615,11 +1625,10 @@ argv_compare(char *const *argv1, char *const *argv2)
* - argv: allocatd array containing {"cmd", "arg1", "arg2", NULL}. Caller frees. * - argv: allocatd array containing {"cmd", "arg1", "arg2", NULL}. Caller frees.
*/ */
static ssize_t static ssize_t
pipe_argv_from_string(const char *value, char **cmd, char ***argv, pipe_argv_from_string(const char *value, char ***argv,
struct config *conf, struct config *conf,
const char *path, unsigned lineno) const char *path, unsigned lineno)
{ {
*cmd = NULL;
*argv = NULL; *argv = NULL;
if (value[0] != '[') if (value[0] != '[')
@ -1632,11 +1641,11 @@ pipe_argv_from_string(const char *value, char **cmd, char ***argv,
} }
size_t pipe_len = pipe_cmd_end - value - 1; size_t pipe_len = pipe_cmd_end - value - 1;
*cmd = xstrndup(&value[1], pipe_len); char *cmd = xstrndup(&value[1], pipe_len);
if (!tokenize_cmdline(*cmd, argv)) { if (!tokenize_cmdline(cmd, argv)) {
LOG_AND_NOTIFY_ERR("%s:%d: syntax error in command line", path, lineno); LOG_AND_NOTIFY_ERR("%s:%d: syntax error in command line", path, lineno);
free(*cmd); free(cmd);
return -1; return -1;
} }
@ -1647,6 +1656,7 @@ pipe_argv_from_string(const char *value, char **cmd, char ***argv,
remove_len++; remove_len++;
} }
free(cmd);
return remove_len; return remove_len;
} }
@ -1657,11 +1667,10 @@ parse_key_binding_section(
struct config_key_binding_list *bindings, struct config_key_binding_list *bindings,
struct config *conf, const char *path, unsigned lineno) struct config *conf, const char *path, unsigned lineno)
{ {
char *pipe_cmd;
char **pipe_argv; char **pipe_argv;
ssize_t pipe_remove_len = pipe_argv_from_string( ssize_t pipe_remove_len = pipe_argv_from_string(
value, &pipe_cmd, &pipe_argv, conf, path, lineno); value, &pipe_argv, conf, path, lineno);
if (pipe_remove_len < 0) if (pipe_remove_len < 0)
return false; return false;
@ -1683,17 +1692,12 @@ parse_key_binding_section(
if (binding->action != action) if (binding->action != action)
continue; continue;
if (binding->pipe.master_copy) { if (binding->pipe.master_copy)
free(binding->pipe.cmd); free_argv(&binding->pipe.argv);
free(binding->pipe.argv);
}
binding->action = BIND_ACTION_NONE; binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
} }
free(pipe_argv); free(pipe_argv);
free(pipe_cmd);
return true; return true;
} }
@ -1705,7 +1709,6 @@ parse_key_binding_section(
path, lineno)) path, lineno))
{ {
free(pipe_argv); free(pipe_argv);
free(pipe_cmd);
free_key_combo_list(&key_combos); free_key_combo_list(&key_combos);
return false; return false;
} }
@ -1715,18 +1718,14 @@ parse_key_binding_section(
struct config_key_binding *binding = &bindings->arr[i]; struct config_key_binding *binding = &bindings->arr[i];
if (binding->action == action && if (binding->action == action &&
((binding->pipe.argv == NULL && pipe_argv == NULL) || ((binding->pipe.argv.args == NULL && pipe_argv == NULL) ||
(binding->pipe.argv != NULL && pipe_argv != NULL && (binding->pipe.argv.args != NULL && pipe_argv != NULL &&
argv_compare(binding->pipe.argv, pipe_argv) == 0))) argv_compare(binding->pipe.argv.args, pipe_argv) == 0)))
{ {
if (binding->pipe.master_copy) { if (binding->pipe.master_copy)
free(binding->pipe.cmd); free_argv(&binding->pipe.argv);
free(binding->pipe.argv);
}
binding->action = BIND_ACTION_NONE; binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
} }
} }
@ -1744,8 +1743,9 @@ parse_key_binding_section(
.modifiers = combo->modifiers, .modifiers = combo->modifiers,
.sym = combo->sym, .sym = combo->sym,
.pipe = { .pipe = {
.cmd = pipe_cmd, .argv = {
.argv = pipe_argv, .args = pipe_argv,
},
.master_copy = first, .master_copy = first,
}, },
}; };
@ -1761,7 +1761,6 @@ parse_key_binding_section(
LOG_AND_NOTIFY_ERR("%s:%u: [%s]: %s: invalid action", LOG_AND_NOTIFY_ERR("%s:%u: [%s]: %s: invalid action",
path, lineno, section, key); path, lineno, section, key);
free(pipe_cmd);
free(pipe_argv); free(pipe_argv);
return false; return false;
} }
@ -1966,12 +1965,12 @@ has_mouse_binding_collisions(struct config *conf, const struct key_combo_list *k
bool count = combo1->count == combo2->m.count; bool count = combo1->count == combo2->m.count;
if (shift && alt && ctrl && meta && button && count) { if (shift && alt && ctrl && meta && button && count) {
bool has_pipe = combo1->pipe.cmd != NULL; bool has_pipe = combo1->pipe.argv.args != NULL;
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'", LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'",
path, lineno, combo2->text, path, lineno, combo2->text,
binding_action_map[combo1->action], binding_action_map[combo1->action],
has_pipe ? " [" : "", has_pipe ? " [" : "",
has_pipe ? combo1->pipe.cmd : "", has_pipe ? combo1->pipe.argv.args[0] : "",
has_pipe ? "]" : ""); has_pipe ? "]" : "");
return true; return true;
} }
@ -1987,11 +1986,10 @@ parse_section_mouse_bindings(
const char *key, const char *value, struct config *conf, const char *key, const char *value, struct config *conf,
const char *path, unsigned lineno, bool errors_are_fatal) const char *path, unsigned lineno, bool errors_are_fatal)
{ {
char *pipe_cmd;
char **pipe_argv; char **pipe_argv;
ssize_t pipe_remove_len = pipe_argv_from_string( ssize_t pipe_remove_len = pipe_argv_from_string(
value, &pipe_cmd, &pipe_argv, conf, path, lineno); value, &pipe_argv, conf, path, lineno);
if (pipe_remove_len < 0) if (pipe_remove_len < 0)
return false; return false;
@ -2015,17 +2013,12 @@ parse_section_mouse_bindings(
&conf->bindings.mouse.arr[i]; &conf->bindings.mouse.arr[i];
if (binding->action == action) { if (binding->action == action) {
if (binding->pipe.master_copy) { if (binding->pipe.master_copy)
free(binding->pipe.cmd); free_argv(&binding->pipe.argv);
free(binding->pipe.argv);
}
binding->action = BIND_ACTION_NONE; binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
} }
} }
free(pipe_argv); free(pipe_argv);
free(pipe_cmd);
return true; return true;
} }
@ -2034,7 +2027,6 @@ parse_section_mouse_bindings(
has_mouse_binding_collisions(conf, &key_combos, path, lineno)) has_mouse_binding_collisions(conf, &key_combos, path, lineno))
{ {
free(pipe_argv); free(pipe_argv);
free(pipe_cmd);
free_key_combo_list(&key_combos); free_key_combo_list(&key_combos);
return false; return false;
} }
@ -2044,17 +2036,13 @@ parse_section_mouse_bindings(
struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i]; struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i];
if (binding->action == action && if (binding->action == action &&
((binding->pipe.argv == NULL && pipe_argv == NULL) || ((binding->pipe.argv.args == NULL && pipe_argv == NULL) ||
(binding->pipe.argv != NULL && pipe_argv != NULL && (binding->pipe.argv.args != NULL && pipe_argv != NULL &&
argv_compare(binding->pipe.argv, pipe_argv) == 0))) argv_compare(binding->pipe.argv.args, pipe_argv) == 0)))
{ {
if (binding->pipe.master_copy) { if (binding->pipe.master_copy)
free(binding->pipe.cmd); free_argv(&binding->pipe.argv);
free(binding->pipe.argv);
}
binding->action = BIND_ACTION_NONE; binding->action = BIND_ACTION_NONE;
binding->pipe.cmd = NULL;
binding->pipe.argv = NULL;
} }
} }
@ -2074,8 +2062,9 @@ parse_section_mouse_bindings(
.button = combo->m.button, .button = combo->m.button,
.count = combo->m.count, .count = combo->m.count,
.pipe = { .pipe = {
.cmd = pipe_cmd, .argv = {
.argv = pipe_argv, .args = pipe_argv,
},
.master_copy = first, .master_copy = first,
}, },
}; };
@ -2090,7 +2079,6 @@ parse_section_mouse_bindings(
LOG_AND_NOTIFY_ERR("%s:%u: [mouse-bindings]: %s: invalid key", path, lineno, key); LOG_AND_NOTIFY_ERR("%s:%u: [mouse-bindings]: %s: invalid key", path, lineno, key);
free(pipe_argv); free(pipe_argv);
free(pipe_cmd);
return false; return false;
} }
@ -2624,7 +2612,7 @@ config_load(struct config *conf, const char *conf_path,
.urgent = false, .urgent = false,
.notify = false, .notify = false,
.command = { .command = {
.argv = NULL, .argv = {.args = NULL},
}, },
.command_focused = false, .command_focused = false,
}, },
@ -2700,7 +2688,7 @@ config_load(struct config *conf, const char *conf_path,
.selection_target = SELECTION_TARGET_PRIMARY, .selection_target = SELECTION_TARGET_PRIMARY,
.hold_at_exit = false, .hold_at_exit = false,
.notify = { .notify = {
.argv = NULL, .argv = {.args = NULL},
}, },
.tweak = { .tweak = {
@ -2746,8 +2734,8 @@ config_load(struct config *conf, const char *conf_path,
} }
tokenize_cmdline("notify-send -a ${app-id} -i ${app-id} ${title} ${body}", tokenize_cmdline("notify-send -a ${app-id} -i ${app-id} ${title} ${body}",
&conf->notify.argv); &conf->notify.argv.args);
tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv); tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args);
static const wchar_t *url_protocols[] = { static const wchar_t *url_protocols[] = {
L"http://", L"http://",
@ -2872,29 +2860,145 @@ config_override_apply(struct config *conf, config_override_t *overrides, bool er
return true; return true;
} }
static void NOINLINE static void
binding_pipe_free(struct config_binding_pipe *pipe) binding_pipe_free(struct config_binding_pipe *pipe)
{ {
if (pipe->master_copy) { if (pipe->master_copy)
free(pipe->cmd); free_argv(&pipe->argv);
free(pipe->argv); }
static void
binding_pipe_clone(struct config_binding_pipe *dst,
const struct config_binding_pipe *src)
{
xassert(src->master_copy);
clone_argv(&dst->argv, &src->argv);
}
static void NOINLINE
key_binding_list_free(struct config_key_binding_list *bindings)
{
for (size_t i = 0; i < bindings->count; i++)
binding_pipe_free(&bindings->arr[i].pipe);
free(bindings->arr);
}
static void NOINLINE
key_binding_list_clone(struct config_key_binding_list *dst,
const struct config_key_binding_list *src)
{
struct argv *last_master_argv = NULL;
dst->count = src->count;
dst->arr = xmalloc(src->count * sizeof(dst->arr[0]));
for (size_t i = 0; i < src->count; i++) {
const struct config_key_binding *old = &src->arr[i];
struct config_key_binding *new = &dst->arr[i];
*new = *old;
if (old->pipe.argv.args == NULL)
continue;
if (old->pipe.master_copy) {
binding_pipe_clone(&new->pipe, &old->pipe);
last_master_argv = &new->pipe.argv;
} else {
xassert(last_master_argv != NULL);
new->pipe.argv = *last_master_argv;
}
} }
} }
static void static void
key_binding_free(struct config_key_binding *binding) mouse_binding_list_free(struct config_mouse_binding_list *bindings)
{
binding_pipe_free(&binding->pipe);
}
static void
key_binding_list_free(struct config_key_binding_list *bindings)
{ {
for (size_t i = 0; i < bindings->count; i++) for (size_t i = 0; i < bindings->count; i++)
key_binding_free(&bindings->arr[i]); binding_pipe_free(&bindings->arr[i].pipe);
free(bindings->arr); free(bindings->arr);
} }
static void NOINLINE
mouse_binding_list_clone(struct config_mouse_binding_list *dst,
const struct config_mouse_binding_list *src)
{
struct argv *last_master_argv = NULL;
dst->count = src->count;
dst->arr = xmalloc(src->count * sizeof(dst->arr[0]));
for (size_t i = 0; i < src->count; i++) {
const struct config_mouse_binding *old = &src->arr[i];
struct config_mouse_binding *new = &dst->arr[i];
*new = *old;
if (old->pipe.argv.args == NULL)
continue;
if (old->pipe.master_copy) {
binding_pipe_clone(&new->pipe, &old->pipe);
last_master_argv = &new->pipe.argv;
} else {
xassert(last_master_argv != NULL);
new->pipe.argv = *last_master_argv;
}
}
}
struct config *
config_clone(const struct config *old)
{
struct config *conf = xmalloc(sizeof(*conf));
*conf = *old;
conf->term = xstrdup(old->term);
conf->shell = xstrdup(old->shell);
conf->title = xstrdup(old->title);
conf->app_id = xstrdup(old->app_id);
conf->word_delimiters = xwcsdup(old->word_delimiters);
conf->scrollback.indicator.text = xwcsdup(old->scrollback.indicator.text);
conf->server_socket_path = xstrdup(old->server_socket_path);
spawn_template_clone(&conf->bell.command, &old->bell.command);
spawn_template_clone(&conf->notify, &old->notify);
for (size_t i = 0; i < ALEN(conf->fonts); i++) {
struct config_font_list *dst = &conf->fonts[i];
const struct config_font_list *src = &old->fonts[i];
dst->count = src->count;
dst->arr = xmalloc(dst->count * sizeof(dst->arr[0]));
for (size_t j = 0; j < dst->count; j++)
dst->arr[j].pattern = xstrdup(src->arr[j].pattern);
}
conf->url.label_letters = xwcsdup(old->url.label_letters);
spawn_template_clone(&conf->url.launch, &old->url.launch);
conf->url.protocols = xmalloc(
old->url.prot_count * sizeof(conf->url.protocols[0]));
for (size_t i = 0; i < old->url.prot_count; i++)
conf->url.protocols[i] = xwcsdup(old->url.protocols[i]);
key_binding_list_clone(&conf->bindings.key, &old->bindings.key);
key_binding_list_clone(&conf->bindings.search, &old->bindings.search);
key_binding_list_clone(&conf->bindings.url, &old->bindings.url);
mouse_binding_list_clone(&conf->bindings.mouse, &old->bindings.mouse);
conf->notifications.length = 0;
conf->notifications.head = conf->notifications.tail = 0;
tll_foreach(old->notifications, it) {
struct user_notification notif = {
.kind = it->item.kind,
.text = xstrdup(it->item.text),
};
tll_push_back(conf->notifications, notif);
}
return conf;
}
void void
config_free(struct config conf) config_free(struct config conf)
{ {
@ -2903,15 +3007,15 @@ config_free(struct config conf)
free(conf.title); free(conf.title);
free(conf.app_id); free(conf.app_id);
free(conf.word_delimiters); free(conf.word_delimiters);
free_spawn_template(&conf.bell.command); spawn_template_free(&conf.bell.command);
free(conf.scrollback.indicator.text); free(conf.scrollback.indicator.text);
free_spawn_template(&conf.notify); spawn_template_free(&conf.notify);
for (size_t i = 0; i < ALEN(conf.fonts); i++) for (size_t i = 0; i < ALEN(conf.fonts); i++)
config_font_list_destroy(&conf.fonts[i]); config_font_list_destroy(&conf.fonts[i]);
free(conf.server_socket_path); free(conf.server_socket_path);
free(conf.url.label_letters); free(conf.url.label_letters);
free_spawn_template(&conf.url.launch); spawn_template_free(&conf.url.launch);
for (size_t i = 0; i < conf.url.prot_count; i++) for (size_t i = 0; i < conf.url.prot_count; i++)
free(conf.url.protocols[i]); free(conf.url.protocols[i]);
free(conf.url.protocols); free(conf.url.protocols);
@ -2919,10 +3023,7 @@ config_free(struct config conf)
key_binding_list_free(&conf.bindings.key); key_binding_list_free(&conf.bindings.key);
key_binding_list_free(&conf.bindings.search); key_binding_list_free(&conf.bindings.search);
key_binding_list_free(&conf.bindings.url); key_binding_list_free(&conf.bindings.url);
mouse_binding_list_free(&conf.bindings.mouse);
for (size_t i = 0; i < conf.bindings.mouse.count; i++)
binding_pipe_free(&conf.bindings.mouse.arr[i].pipe);
free(conf.bindings.mouse.arr);
user_notifications_free(&conf.notifications); user_notifications_free(&conf.notifications);
} }

View file

@ -37,9 +37,12 @@ struct config_key_modifiers {
bool meta; bool meta;
}; };
struct argv {
char **args;
};
struct config_binding_pipe { struct config_binding_pipe {
char *cmd; struct argv argv;
char **argv;
bool master_copy; bool master_copy;
}; };
@ -63,7 +66,7 @@ DEFINE_LIST(struct config_mouse_binding);
typedef tll(char *) config_override_t; typedef tll(char *) config_override_t;
struct config_spawn_template { struct config_spawn_template {
char **argv; struct argv argv;
}; };
struct config { struct config {
@ -262,6 +265,7 @@ bool config_load(
user_notifications_t *initial_user_notifications, user_notifications_t *initial_user_notifications,
config_override_t *overrides, bool errors_are_fatal); config_override_t *overrides, bool errors_are_fatal);
void config_free(struct config conf); void config_free(struct config conf);
struct config *config_clone(const struct config *old);
bool config_font_parse(const char *pattern, struct config_font *font); bool config_font_parse(const char *pattern, struct config_font *font);
void config_font_list_destroy(struct config_font_list *font_list); void config_font_list_destroy(struct config_font_list *font_list);

View file

@ -519,7 +519,7 @@ convert_key_binding(const struct seat *seat,
.sym = sym, .sym = sym,
.key_codes = key_codes_for_xkb_sym(seat->kbd.xkb_keymap, sym), .key_codes = key_codes_for_xkb_sym(seat->kbd.xkb_keymap, sym),
.action = conf_binding->action, .action = conf_binding->action,
.pipe_argv = conf_binding->pipe.argv, .pipe_argv = conf_binding->pipe.argv.args,
}; };
tll_push_back(*bindings, binding); tll_push_back(*bindings, binding);
} }
@ -568,7 +568,7 @@ convert_mouse_binding(struct seat *seat,
.mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers), .mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers),
.button = conf_binding->button, .button = conf_binding->button,
.count = conf_binding->count, .count = conf_binding->count,
.pipe_argv = conf_binding->pipe.argv, .pipe_argv = conf_binding->pipe.argv.args,
}; };
tll_push_back(seat->mouse.bindings, binding); tll_push_back(seat->mouse.bindings, binding);
} }
@ -1970,7 +1970,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
if (match != NULL) { if (match != NULL) {
consumed = execute_binding( consumed = execute_binding(
seat, term, match->action, match->pipe.argv, serial); seat, term, match->action, match->pipe.argv.args,
serial);
} }
} }
} }

View file

@ -27,7 +27,7 @@ notify_notify(const struct terminal *term, const char *title, const char *body)
if (title == NULL || body == NULL) if (title == NULL || body == NULL)
return; return;
if (term->conf->notify.argv == NULL) if (term->conf->notify.argv.args == NULL)
return; return;
char **argv = NULL; char **argv = NULL;

View file

@ -114,7 +114,7 @@ spawn_expand_template(const struct config_spawn_template *template,
*argc = 0; *argc = 0;
*argv = NULL; *argv = NULL;
for (; template->argv[*argc] != NULL; (*argc)++) for (; template->argv.args[*argc] != NULL; (*argc)++)
; ;
#define append(s, n) \ #define append(s, n) \
@ -133,7 +133,7 @@ spawn_expand_template(const struct config_spawn_template *template,
char *expanded = NULL; char *expanded = NULL;
char *start = NULL; char *start = NULL;
char *last_end = template->argv[i]; char *last_end = template->argv.args[i];
while ((start = strstr(last_end, "${")) != NULL) { while ((start = strstr(last_end, "${")) != NULL) {
/* Append everything from the last template's end to this /* Append everything from the last template's end to this
@ -173,7 +173,9 @@ spawn_expand_template(const struct config_spawn_template *template,
last_end = end + 1; last_end = end + 1;
} }
append(last_end, template->argv[i] + strlen(template->argv[i]) - last_end); append(
last_end,
template->argv.args[i] + strlen(template->argv.args[i]) - last_end);
(*argv)[i] = expanded; (*argv)[i] = expanded;
} }
(*argv)[*argc] = NULL; (*argv)[*argc] = NULL;

View file

@ -2677,11 +2677,11 @@ term_bell(struct terminal *term)
notify_notify(term, "Bell", "Bell in terminal"); notify_notify(term, "Bell", "Bell in terminal");
} }
if ((term->conf->bell.command.argv != NULL) && if ((term->conf->bell.command.argv.args != NULL) &&
(!term->kbd_focus || term->conf->bell.command_focused)) (!term->kbd_focus || term->conf->bell.command_focused))
{ {
int devnull = open("/dev/null", O_RDONLY); int devnull = open("/dev/null", O_RDONLY);
spawn(term->reaper, NULL, term->conf->bell.command.argv, devnull, -1, -1); spawn(term->reaper, NULL, term->conf->bell.command.argv.args, devnull, -1, -1);
if (devnull >= 0) if (devnull >= 0)
close(devnull); close(devnull);