From 8389047b47bbae0320290820fe93e4aaef134f07 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 10 Dec 2018 23:20:27 +0100 Subject: [PATCH] Introduce shell_expand This performs shell expansion without field splitting. This fixes issues with filenames containing spaces. This commit also cleans up complicated logic to join fields and to set XDG_CONFIG_HOME. --- common/meson.build | 5 +-- common/shexp.c | 51 +++++++++++++++++++++++++++ include/shexp.h | 14 ++++++++ sway/commands/output/background.c | 31 ++++------------- sway/config.c | 58 ++++++++++++++----------------- sway/config/bar.c | 1 - swaylock/main.c | 43 +++++++---------------- swaynag/config.c | 32 +++++------------ 8 files changed, 121 insertions(+), 114 deletions(-) create mode 100644 common/shexp.c create mode 100644 include/shexp.h diff --git a/common/meson.build b/common/meson.build index 224a9c3fc..3b51f9800 100644 --- a/common/meson.build +++ b/common/meson.build @@ -4,14 +4,15 @@ lib_sway_common = static_library( 'background-image.c', 'cairo.c', 'ipc-client.c', + 'list.c', 'log.c', 'loop.c', - 'list.c', 'pango.c', 'readline.c', + 'shexp.c', 'stringop.c', 'unicode.c', - 'util.c' + 'util.c', ), dependencies: [ cairo, diff --git a/common/shexp.c b/common/shexp.c new file mode 100644 index 000000000..4118c4e7a --- /dev/null +++ b/common/shexp.c @@ -0,0 +1,51 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include "shexp.h" + +static const char escaped_quote[] = "'\"'\"'"; + +static char *escape(const char *str) { + size_t escaped_len = 2; // quotes at the begining and end + for (size_t i = 0; str[i] != '\0'; ++i) { + if (str[i] == '\'') { + escaped_len += strlen(escaped_quote); + } else { + ++escaped_len; + } + } + + char *escaped = malloc(escaped_len + 1); + escaped[0] = '\''; + size_t j = 1; + for (size_t i = 0; str[i] != '\0'; ++i) { + if (str[i] == '\'') { + memcpy(&escaped[j], escaped_quote, strlen(escaped_quote)); + j += strlen(escaped_quote); + } else { + escaped[j] = str[i]; + ++j; + } + } + escaped[j] = '\''; + escaped[j + 1] = '\0'; + + return escaped; +} + +bool shell_expand(char **path) { + char *escaped_path = escape(*path); + + wordexp_t p; + bool ret = false; + if (wordexp(escaped_path, &p, WRDE_UNDEF) == 0) { + free(*path); + *path = strdup(p.we_wordv[0]); + wordfree(&p); + ret = true; + } + + free(escaped_path); + return ret; +} diff --git a/include/shexp.h b/include/shexp.h new file mode 100644 index 000000000..035100f00 --- /dev/null +++ b/include/shexp.h @@ -0,0 +1,14 @@ +#ifndef _SWAY_SHEXP_H +#define _SWAY_SHEXP_H + +#include + +/** + * Takes a pointer to a string and reallocates it with the result of its shell + * expansion. Undefined environment variables are considered as errors. Returns + * true if it succeeds, otherwise returns false, leaving the original string + * unchanged. + */ +bool shell_expand(char **path); + +#endif diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 2cd1b76af..80c5704b3 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -1,14 +1,14 @@ #define _POSIX_C_SOURCE 200809 +#include #include #include #include -#include -#include +#include "log.h" +#include "shexp.h" +#include "stringop.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/swaynag.h" -#include "log.h" -#include "stringop.h" static const char *bg_options[] = { "stretch", @@ -61,28 +61,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { "Missing background scaling mode."); } - wordexp_t p = {0}; char *src = join_args(argv, j); - while (strstr(src, " ")) { - src = realloc(src, strlen(src) + 2); - char *ptr = strstr(src, " ") + 1; - memmove(ptr + 1, ptr, strlen(ptr) + 1); - *ptr = '\\'; - } - if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { - struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, "output", + if (!shell_expand(&src)) { + return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); - free(src); - wordfree(&p); - return cmd_res; - } - free(src); - src = join_args(p.we_wordv, p.we_wordc); - wordfree(&p); - if (!src) { - wlr_log(WLR_ERROR, "Failed to duplicate string"); - return cmd_results_new(CMD_FAILURE, "output", - "Unable to allocate resource"); } if (config->reading && *src != '/') { @@ -154,4 +136,3 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { config->handler_context.leftovers.argv = argv; return NULL; } - diff --git a/sway/config.c b/sway/config.c index 5b03bc563..5b193e150 100644 --- a/sway/config.c +++ b/sway/config.c @@ -1,39 +1,40 @@ #define _XOPEN_SOURCE 600 // for realpath -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include #include #include -#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #ifdef __linux__ #include #elif __FreeBSD__ #include #endif #include -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" +#include "cairo.h" +#include "list.h" +#include "log.h" +#include "pango.h" +#include "readline.h" +#include "shexp.h" +#include "stringop.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/criteria.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" #include "sway/swaynag.h" #include "sway/tree/arrange.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" -#include "cairo.h" -#include "pango.h" -#include "readline.h" -#include "stringop.h" -#include "list.h" -#include "log.h" struct sway_config *config = NULL; @@ -321,15 +322,11 @@ static char *get_config_path(void) { } for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { - wordexp_t p; - if (wordexp(config_paths[i], &p, WRDE_UNDEF) == 0) { - char *path = strdup(p.we_wordv[0]); - wordfree(&p); - if (file_exists(path)) { - return path; - } - free(path); + char *path = strdup(config_paths[i]); + if (shell_expand(&path) && file_exists(path)) { + return path; } + free(path); } return NULL; @@ -545,17 +542,14 @@ bool load_include_configs(const char *path, struct sway_config *config, } wordexp_t p; - - if (wordexp(path, &p, 0) < 0) { + if (wordexp(path, &p, WRDE_UNDEF) < 0) { free(parent_path); free(wd); return false; } - char **w = p.we_wordv; - size_t i; - for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config, swaynag); + for (size_t i = 0; i < p.we_wordc; ++i) { + load_include_config(p.we_wordv[i], parent_dir, config, swaynag); } free(parent_path); wordfree(&p); diff --git a/sway/config/bar.c b/sway/config/bar.c index 45c9e9980..f98498deb 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/swaylock/main.c b/swaylock/main.c index 9aeb4e64e..edf4fc002 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -14,17 +14,17 @@ #include #include #include -#include #include -#include "swaylock/seat.h" -#include "swaylock/swaylock.h" #include "background-image.h" -#include "pool-buffer.h" #include "cairo.h" #include "log.h" #include "loop.h" +#include "pool-buffer.h" #include "readline.h" +#include "shexp.h" #include "stringop.h" +#include "swaylock/seat.h" +#include "swaylock/swaylock.h" #include "util.h" #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -375,12 +375,7 @@ static void load_image(char *arg, struct swaylock_state *state) { } // Bash doesn't replace the ~ with $HOME if the output name is supplied - wordexp_t p; - if (wordexp(image->path, &p, 0) == 0) { - free(image->path); - image->path = strdup(p.we_wordv[0]); - wordfree(&p); - } + shell_expand(&image->path); // Load the actual image image->cairo_surface = load_background_image(image->path); @@ -771,31 +766,17 @@ static char *get_config_path(void) { SYSCONFDIR "/swaylock/config", }; - if (!getenv("XDG_CONFIG_HOME")) { - char *home = getenv("HOME"); - char *config_home = malloc(strlen(home) + strlen("/.config") + 1); - if (!config_home) { - wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); - } else { - strcpy(config_home, home); - strcat(config_home, "/.config"); - setenv("XDG_CONFIG_HOME", config_home, 1); - wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); - free(config_home); - } + char *config_home = getenv("XDG_CONFIG_HOME"); + if (config_home == NULL || config_home[0] == '\0') { + config_paths[1] = "$HOME/.config/swaylock/config"; } - wordexp_t p; - char *path; for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { - if (wordexp(config_paths[i], &p, 0) == 0) { - path = strdup(p.we_wordv[0]); - wordfree(&p); - if (file_exists(path)) { - return path; - } - free(path); + char *path = strdup(config_paths[i]); + if (shell_expand(&path) && file_exists(path)) { + return path; } + free(path); } return NULL; diff --git a/swaynag/config.c b/swaynag/config.c index e724aa0c1..463cd3e72 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -1,10 +1,10 @@ #define _POSIX_C_SOURCE 200809L #include #include -#include -#include "log.h" #include "list.h" +#include "log.h" #include "readline.h" +#include "shexp.h" #include "swaynag/swaynag.h" #include "swaynag/types.h" #include "util.h" @@ -306,31 +306,17 @@ char *swaynag_get_config_path(void) { SYSCONFDIR "/swaynag/config", }; - if (!getenv("XDG_CONFIG_HOME")) { - char *home = getenv("HOME"); - char *config_home = malloc(strlen(home) + strlen("/.config") + 1); - if (!config_home) { - wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); - } else { - strcpy(config_home, home); - strcat(config_home, "/.config"); - setenv("XDG_CONFIG_HOME", config_home, 1); - wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); - free(config_home); - } + char *config_home = getenv("XDG_CONFIG_HOME"); + if (config_home == NULL || config_home[0] == '\0') { + config_paths[1] = "$HOME/.config/swaynag/config"; } - wordexp_t p; - char *path; for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { - if (wordexp(config_paths[i], &p, 0) == 0) { - path = strdup(p.we_wordv[0]); - wordfree(&p); - if (file_exists(path)) { - return path; - } - free(path); + char *path = strdup(config_paths[i]); + if (shell_expand(&path) && file_exists(path)) { + return path; } + free(path); } return NULL;