summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--completions/tofi3
-rw-r--r--doc/config7
-rw-r--r--doc/tofi.5.md10
-rw-r--r--doc/tofi.5.scd8
-rw-r--r--src/config.c31
-rw-r--r--src/main.c2
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
diff --git a/doc/config b/doc/config
index e1e93a7..f95a367 100644
--- a/doc/config
+++ b/doc/config
@@ -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);
diff --git a/src/main.c b/src/main.c
index 673bb9f..0a9cdf1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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},