diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | README.7 | 4 | ||||
-rw-r--r-- | bounce.h | 5 | ||||
-rw-r--r-- | xdg.c | 150 |
4 files changed, 159 insertions, 1 deletions
diff --git a/Makefile b/Makefile index a325b8a..0fcadf5 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ OBJS += local.o OBJS += ring.o OBJS += server.o OBJS += state.o +OBJS += xdg.o dev: tags all diff --git a/README.7 b/README.7 index e926b81..221ffdb 100644 --- a/README.7 +++ b/README.7 @@ -1,4 +1,4 @@ -.Dd August 12, 2020 +.Dd August 24, 2020 .Dt README 7 .Os "Causal Agency" . @@ -124,6 +124,8 @@ buffer between server and clients .It Pa config.c .Xr getopt_long 3 Ns -integrated configuration parsing +.It Pa xdg.c +XDG base directories .It Pa dispatch.c SNI socket dispatch .It Pa rc.d/ diff --git a/bounce.h b/bounce.h index 986bd5a..b09a349 100644 --- a/bounce.h +++ b/bounce.h @@ -208,6 +208,11 @@ void stateSync(struct Client *client); const char *stateNick(void); const char *stateEcho(void); +const char *configPath(const char **dirs, const char *path); +const char *dataPath(const char **dirs, const char *path); +FILE *configOpen(const char *path, const char *mode); +FILE *dataOpen(const char *path, const char *mode); + struct option; int getopt_config( int argc, char *const *argv, diff --git a/xdg.c b/xdg.c new file mode 100644 index 0000000..7400d80 --- /dev/null +++ b/xdg.c @@ -0,0 +1,150 @@ +/* Copyright (C) 2019, 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with OpenSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of OpenSSL used as well as that of the + * covered work. + */ + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "bounce.h" + +#define SUBDIR "pounce" + +struct Base { + const char *envHome; + const char *envDirs; + const char *defHome; + const char *defDirs; +}; + +static const struct Base Config = { + .envHome = "XDG_CONFIG_HOME", + .envDirs = "XDG_CONFIG_DIRS", + .defHome = ".config", + .defDirs = "/etc/xdg", +}; + +static const struct Base Data = { + .envHome = "XDG_DATA_HOME", + .envDirs = "XDG_DATA_DIRS", + .defHome = ".local/share", + .defDirs = "/usr/local/share:/usr/share", +}; + +static const char * +basePath(struct Base base, const char **dirs, const char *path) { + static char buf[PATH_MAX]; + + if (*dirs) { + if (!**dirs) return NULL; + size_t len = strcspn(*dirs, ":"); + snprintf(buf, sizeof(buf), "%.*s/" SUBDIR "/%s", (int)len, *dirs, path); + *dirs += len; + if (**dirs) *dirs += 1; + return buf; + } + + if (path[0] == '/' || path[0] == '.') { + *dirs = ""; + return path; + } + + *dirs = getenv(base.envDirs); + if (!*dirs) *dirs = base.defDirs; + + const char *home = getenv("HOME"); + const char *baseHome = getenv(base.envHome); + if (baseHome) { + snprintf(buf, sizeof(buf), "%s/" SUBDIR "/%s", baseHome, path); + } else if (home) { + snprintf( + buf, sizeof(buf), "%s/%s/" SUBDIR "/%s", + home, base.defHome, path + ); + } else { + return NULL; + } + return buf; +} + +const char *configPath(const char **dirs, const char *path) { + return basePath(Config, dirs, path); +} + +const char * +dataPath(const char **dirs, const char *path) { + return basePath(Data, dirs, path); +} + +FILE *configOpen(const char *path, const char *mode) { + const char *dirs = NULL; + for (const char *abs; NULL != (abs = configPath(&dirs, path));) { + FILE *file = fopen(abs, mode); + if (file) return file; + if (errno != ENOENT) warn("%s", abs); + } + FILE *file = fopen(path, mode); + if (!file) warn("%s", path); + return file; +} + +static void dataMkdir(const char *path) { + const char *dirs = NULL; + const char *abs = dataPath(&dirs, path); + if (!abs) return; + int error = mkdir(abs, S_IRWXU); + if (error && errno != EEXIST) warn("%s", abs); +} + +FILE *dataOpen(const char *path, const char *mode) { + const char *dirs = NULL; + for (const char *abs; NULL != (abs = dataPath(&dirs, path));) { + FILE *file = fopen(abs, mode); + if (file) return file; + if (errno != ENOENT) warn("%s", abs); + } + + if (mode[0] != 'r') { + dataMkdir(""); + dirs = NULL; + path = dataPath(&dirs, path); + if (!path) { + warn("HOME unset"); + return NULL; + } + FILE *file = fopen(path, mode); + if (!file) warn("%s", path); + return file; + } + + FILE *file = fopen(path, mode); + if (!file) warn("%s", path); + return file; +} |