diff options
author | Phil Jones <philj56@gmail.com> | 2022-09-27 14:34:03 +0100 |
---|---|---|
committer | Phil Jones <philj56@gmail.com> | 2022-09-27 14:34:03 +0100 |
commit | 4801fff08bf3d42361a72e339c59390828983f49 (patch) | |
tree | fc23e1fa0a954181159ce1ad8bf17546259e56f1 | |
parent | 452c1b6fa737661358cd2d6ce47d1a6400039f53 (diff) |
Add --include option.
This allows config files to include other files, so you can e.g. split
style and behaviour options into different files.
-rw-r--r-- | completions/tofi | 3 | ||||
-rw-r--r-- | doc/config | 7 | ||||
-rw-r--r-- | doc/tofi.5.md | 10 | ||||
-rw-r--r-- | doc/tofi.5.scd | 8 | ||||
-rw-r--r-- | src/config.c | 31 | ||||
-rw-r--r-- | src/main.c | 2 |
6 files changed, 60 insertions, 1 deletions
diff --git a/completions/tofi b/completions/tofi index 3ab6c32..0e21eed 100644 --- a/completions/tofi +++ b/completions/tofi @@ -8,6 +8,7 @@ _tofi() words=( --help --config + --include --output --scale --anchor @@ -52,6 +53,8 @@ _tofi() case "${prev}" in --font) ;& + --include) + ;& --config|-c) _filedir return 0 @@ -144,3 +144,10 @@ # for a short time after launch. The only reason to use this option is # performance on slow systems. late-keyboard-init = false + +# +### Inclusion +# + # Configs can be split between multiple files, and then included + # within each other. + # include = /path/to/config diff --git a/doc/tofi.5.md b/doc/tofi.5.md index 8455667..b330278 100644 --- a/doc/tofi.5.md +++ b/doc/tofi.5.md @@ -20,6 +20,16 @@ case-insensitive, except where not possible (e.g. paths). Later options override earlier options, and command line options override config file options. +# SPECIAL OPTIONS + +**include**=*path* + +> Include the contents of another config file. If *path* is a relative +> path, it is interpreted as relative to this config file's path (or the +> current directory if **--include** is passed on the command line). +> Inclusion happens immediately, before the rest of the current file's +> contents are parsed. + # BEHAVIOUR OPTIONS **hide-cursor**=*true\|false* diff --git a/doc/tofi.5.scd b/doc/tofi.5.scd index 696b06e..e0faa2d 100644 --- a/doc/tofi.5.scd +++ b/doc/tofi.5.scd @@ -22,6 +22,14 @@ case-insensitive, except where not possible (e.g. paths). Later options override earlier options, and command line options override config file options. +# SPECIAL OPTIONS + +*include*=_path_ + Include the contents of another config file. If _path_ is a relative path, + it is interpreted as relative to this config file's path (or the current + directory if *--include* is passed on the command line). Inclusion happens + immediately, before the rest of the current file's contents are parsed. + # BEHAVIOUR OPTIONS *hide-cursor*=_true|false_ diff --git a/src/config.c b/src/config.c index 7e5fb6b..fb00fd6 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,6 @@ #include <ctype.h> #include <errno.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -14,6 +15,9 @@ /* Maximum number of config file errors before we give up */ #define MAX_ERRORS 5 +/* Maximum inclusion recursion depth before we give up */ +#define MAX_RECURSION 32 + /* Anyone with a 10M config file is doing something very wrong */ #define MAX_CONFIG_SIZE (10*1024*1024) @@ -63,6 +67,17 @@ void config_load(struct tofi *tofi, const char *filename) } filename = default_filename; } + /* + * Track and limit recursion depth, so we don't overflow the stack if + * a config file loop is created. + */ + static uint8_t recursion_depth = 0; + recursion_depth++; + if (recursion_depth > MAX_RECURSION) { + log_error("Refusing to load %s, recursion too deep (>%u layers).\n", filename, MAX_RECURSION); + recursion_depth--; + return; + } char *config; FILE *fp = fopen(filename, "rb"); if (!fp) { @@ -205,6 +220,7 @@ CLEANUP_FILENAME: if (default_filename) { free(default_filename); } + recursion_depth--; } char *strip(const char *str) @@ -238,7 +254,20 @@ bool parse_option(struct tofi *tofi, const char *filename, size_t lineno, const { bool err = false; struct uint32_percent percent; - if (strcasecmp(option, "anchor") == 0) { + if (strcasecmp(option, "include") == 0) { + if (value[0] == '/') { + config_load(tofi, value); + } else { + char *tmp = xstrdup(filename); + char *dir = dirname(tmp); + size_t len = strlen(dir) + 1 + strlen(value) + 1; + char *config = xcalloc(len, 1); + snprintf(config, len, "%s/%s", dir, value); + config_load(tofi, config); + free(config); + free(tmp); + } + } else if (strcasecmp(option, "anchor") == 0) { tofi->anchor = parse_anchor(filename, lineno, value, &err); } else if (strcasecmp(option, "background-color") == 0) { tofi->window.entry.background_color = parse_color(filename, lineno, value, &err); @@ -775,6 +775,7 @@ static void usage() "\n" " -h, --help Print this message and exit.\n" " -c, --config <path> Specify a config file.\n" +" --include <path> Include an additional config file.\n" " --font <name|path> Font to use.\n" " --font-size <pt> Point size of text.\n" " --background-color <color> Color of the background.\n" @@ -828,6 +829,7 @@ static void usage() const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"config", required_argument, NULL, 'c'}, + {"include", required_argument, NULL, 0}, {"anchor", required_argument, NULL, 0}, {"background-color", required_argument, NULL, 0}, {"corner-radius", required_argument, NULL, 0}, |