Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ Run:
swindings
```

### Options


| Flag | Description |
|------|-------------|
| `-i`, `--follow-includes` | Resolve `include` directives in the sway config file. Glob patterns are supported (e.g. `include conf.d/*.conf`). Disabled by default. |


**Example** — read keybindings from a config that uses `include`:

```bash
swindings --follow-includes
```


## Theming

Expand Down
11 changes: 10 additions & 1 deletion src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ static struct cag_option options[] = {
.access_name = "sway-config",
.value_name = "FILE",
.description = "Path to sway config. Defaults to None. When None, sway "
"path resolution is followed."}};
"path resolution is followed."},
{.identifier = 'i',
.access_letters = "i",
.access_name = "follow-includes",
.value_name = NULL,
.description = "Resolve 'include' directives in the sway config file."}};

static void init_args(cli_args *args) {
args->help = false;
args->version = false;
args->config = NULL;
args->sway_config = NULL;
args->follow_includes = false;
}

bool parse_cli(int argc, char **argv, cli_args *args) {
Expand All @@ -60,6 +66,9 @@ bool parse_cli(int argc, char **argv, cli_args *args) {
case 's':
args->sway_config = (char *)cag_option_get_value(&context);
break;
case 'i':
args->follow_includes = true;
break;
case '?':
cag_option_print_error(&context, stderr);
return false;
Expand Down
1 change: 1 addition & 0 deletions src/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ typedef struct {
bool version;
char *config;
char *sway_config;
bool follow_includes;
} cli_args;

/**
Expand Down
44 changes: 40 additions & 4 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "structures.h"
#include "utils.h"
#include <ctype.h>
#include <glob.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
Expand All @@ -21,7 +23,8 @@ static void capitalize_into(const char *src, char *buf) {
buf[0] = (char)toupper((unsigned char)buf[0]);
}

config_error_t config_read_file(const char *filepath, stringlist_t *out) {
config_error_t config_read_file(const char *filepath, stringlist_t *out,
bool follow_includes) {
if (filepath == NULL || out == NULL) {
return CONFIG_ERR_INVALID_ARGUMENT;
}
Expand All @@ -43,6 +46,41 @@ config_error_t config_read_file(const char *filepath, stringlist_t *out) {
if (*pos == '\0' || *pos == '#')
continue;

if (follow_includes && strncmp(pos, "include ", 8) == 0) {
char *pattern = pos + 8;
while (*pattern == ' ' || *pattern == '\t')
pattern++;
glob_t globbuf;
char *expanded = NULL;
if (pattern[0] == '~') {
const char *home = getenv("HOME");
if (!home) {
return CONFIG_ERR_ENV_FAILED;
}
if (asprintf(&expanded, "%s%s", home, pattern + 1) < 0) {
return CONFIG_ERR_ALLOC_FAILED;
}
pattern = expanded;
}
int ret = glob(pattern, GLOB_NOCHECK, NULL, &globbuf);
free(expanded);
if (ret != 0 && ret != GLOB_NOMATCH) {
globfree(&globbuf);
free(line);
int err = fclose(fp);
if (err)
return CONFIG_ERR_IO;

return CONFIG_ERR_GLOB_FAILED;
}
for (size_t gi = 0; gi < globbuf.gl_pathc; gi++) {
/* Silently skip missing files from glob expansions */
config_read_file(globbuf.gl_pathv[gi], out, follow_includes);
}
globfree(&globbuf);
continue;
}

if (strncmp(pos, PATTERN, sizeof(PATTERN) - 1) == 0 &&
pos[sizeof(PATTERN) - 1] == ' ') {
if (stringlist_append(out, pos) != 0) {
Expand Down Expand Up @@ -87,7 +125,7 @@ char *config_get_sway_filepath(void) {
return NULL;
config_home = config_home_fallback;
}
// NOTE: Copied (with modifications) from
// NOTE: Copied from
// https://github.com/swaywm/sway/blob/f1b40bc288f3be3bcc6a3c71f28ca9bb2529e70b/sway/config.c
struct config_path {
const char *prefix;
Expand All @@ -99,9 +137,7 @@ char *config_get_sway_filepath(void) {
{.prefix = config_home, .config_folder = "sway"},
{.prefix = home, .config_folder = ".i3"},
{.prefix = config_home, .config_folder = "i3"},
// NOTE: Different from original
{.prefix = SYSCONFDIR, .config_folder = "sway"},
// NOTE: Different from original
{.prefix = SYSCONFDIR, .config_folder = "i3"}};

size_t num_config_paths = sizeof(config_paths) / sizeof(config_paths[0]);
Expand Down
5 changes: 4 additions & 1 deletion src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#endif


#include <stdbool.h>
#include <stddef.h>
#include "structures.h"

Expand All @@ -22,6 +23,8 @@ typedef enum {
CONFIG_ERR_INVALID_ARGUMENT,
CONFIG_ERR_ALLOC_FAILED,
CONFIG_ERR_IO,
CONFIG_ERR_GLOB_FAILED,
CONFIG_ERR_ENV_FAILED,
} config_error_t;

typedef struct {
Expand Down Expand Up @@ -56,7 +59,7 @@ void keymaplist_free(KeyMapList *list);
void keymap_free(KeyMap *km);


config_error_t config_read_file(const char *filepath, stringlist_t *out);
config_error_t config_read_file(const char *filepath, stringlist_t *out, bool follow_includes);

// Returns the path to the sway config file, or NULL on error.
// Caller must free() the returned string.
Expand Down
2 changes: 1 addition & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ int main(int argc, char *argv[]) {
return ConfigError;
}

if (config_read_file(filepath, &list) != 0) {
if (config_read_file(filepath, &list, args.follow_includes) != 0) {
if (fprintf(stderr, "failed to read file\n"))
return IOError;
free(filepath);
Expand Down
8 changes: 8 additions & 0 deletions tests/all_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ extern void test_sway_filepath_falls_back_to_i3(void);
extern void test_sway_filepath_none_exist_returns_null(void);
extern void test_sway_filepath_no_home_no_xdg_returns_null(void);
extern void test_sway_filepath_xdg_config_home_i3(void);
extern void test_include_ignored_when_follow_includes_false(void);
extern void test_include_resolved_when_follow_includes_true(void);
extern void test_include_glob_resolves_multiple_files(void);
extern void test_include_missing_file_is_silently_skipped(void);

int main(void) {
UNITY_BEGIN();
Expand Down Expand Up @@ -91,6 +95,10 @@ int main(void) {
RUN_TEST(test_sway_filepath_none_exist_returns_null);
RUN_TEST(test_sway_filepath_no_home_no_xdg_returns_null);
RUN_TEST(test_sway_filepath_xdg_config_home_i3);
RUN_TEST(test_include_ignored_when_follow_includes_false);
RUN_TEST(test_include_resolved_when_follow_includes_true);
RUN_TEST(test_include_glob_resolves_multiple_files);
RUN_TEST(test_include_missing_file_is_silently_skipped);

return UNITY_END();
}
Loading
Loading