diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.7 | 4 | ||||
-rw-r--r-- | config.c | 45 | ||||
-rw-r--r-- | database.h | 15 | ||||
-rw-r--r-- | litterbox.c | 5 | ||||
-rw-r--r-- | xdg.c | 147 |
6 files changed, 169 insertions, 49 deletions
diff --git a/Makefile b/Makefile index 19dc3b1..46796b7 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RCS = rc.d/litterbox -include config.mk FORMATS = generic catgirl irc textual -OBJS.litterbox = litterbox.o config.o +OBJS.litterbox = litterbox.o config.o xdg.o dev: tags all test diff --git a/README.7 b/README.7 index 16943a9..5a0f743 100644 --- a/README.7 +++ b/README.7 @@ -1,4 +1,4 @@ -.Dd August 10, 2020 +.Dd August 20, 2020 .Dt README 7 .Os "Causal Agency" . @@ -88,6 +88,8 @@ log file processing .It Pa config.c .Xr getopt_long 3 Ns -integrated configuration parsing +.It Pa xdg.c +XDG base directories .It Pa rc.d/ .Fx .Xr rc 8 diff --git a/config.c b/config.c index 60359d8..b8b4efa 100644 --- a/config.c +++ b/config.c @@ -33,43 +33,7 @@ #include <stdlib.h> #include <string.h> -#define CONFIG_DIR "litterbox" - -static FILE *find(const char *path, const char *mode) { - if (path[0] == '/' || path[0] == '.') goto local; - - const char *home = getenv("HOME"); - const char *configHome = getenv("XDG_CONFIG_HOME"); - const char *configDirs = getenv("XDG_CONFIG_DIRS"); - - char buf[PATH_MAX]; - if (configHome) { - snprintf(buf, sizeof(buf), "%s/" CONFIG_DIR "/%s", configHome, path); - } else { - if (!home) goto local; - snprintf(buf, sizeof(buf), "%s/.config/" CONFIG_DIR "/%s", home, path); - } - FILE *file = fopen(buf, mode); - if (file) return file; - if (errno != ENOENT) return NULL; - - if (!configDirs) configDirs = "/etc/xdg"; - while (*configDirs) { - size_t len = strcspn(configDirs, ":"); - snprintf( - buf, sizeof(buf), "%.*s/" CONFIG_DIR "/%s", - (int)len, configDirs, path - ); - file = fopen(buf, mode); - if (file) return file; - if (errno != ENOENT) return NULL; - configDirs += len; - if (*configDirs) configDirs++; - } - -local: - return fopen(path, mode); -} +#include "database.h" #define WS "\t " @@ -102,11 +66,8 @@ int getopt_config( if (optind < argc) { num = 0; path = argv[optind++]; - file = find(path, "r"); - if (!file) { - warn("%s", path); - return clean('?'); - } + file = configOpen(path, "r"); + if (!file) return clean('?'); } else { return clean(-1); } diff --git a/database.h b/database.h index fbef0bb..d2db1f0 100644 --- a/database.h +++ b/database.h @@ -27,6 +27,7 @@ #include <err.h> #include <errno.h> +#include <getopt.h> #include <limits.h> #include <sqlite3.h> #include <stdbool.h> @@ -40,6 +41,20 @@ #define SQL(...) #__VA_ARGS__ #define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) +const char *configPath( + char *buf, size_t cap, const char **dirs, const char *path +); +const char *dataPath( + char *buf, size_t cap, const char **dirs, const char *path +); +FILE *configOpen(const char *path, const char *mode); +FILE *dataOpen(const char *path, const char *mode); +void dataMkdir(const char *path); +int getopt_config( + int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longindex +); + #define DATABASE_PATH "litterbox/litterbox.sqlite" enum { DatabaseVersion = 5 }; diff --git a/litterbox.c b/litterbox.c index 2dee87d..ffd838c 100644 --- a/litterbox.c +++ b/litterbox.c @@ -39,11 +39,6 @@ #include "database.h" -int getopt_config( - int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longindex -); - static const char *host; static const char *port = "6697"; static struct tls *client; diff --git a/xdg.c b/xdg.c new file mode 100644 index 0000000..ded02af --- /dev/null +++ b/xdg.c @@ -0,0 +1,147 @@ +/* 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> + +#define SUBDIR "litterbox" + +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, + char *buf, size_t cap, const char **dirs, const char *path +) { + if (*dirs) { + if (!**dirs) return NULL; + size_t len = strcspn(*dirs, ":"); + snprintf(buf, cap, "%.*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, cap, "%s/" SUBDIR "/%s", baseHome, path); + } else { + if (!home) return NULL; + snprintf(buf, cap, "%s/%s/" SUBDIR "/%s", home, base.defHome, path); + } + return buf; +} + +const char * +configPath(char *buf, size_t cap, const char **dirs, const char *path) { + return basePath(Config, buf, cap, dirs, path); +} + +const char * +dataPath(char *buf, size_t cap, const char **dirs, const char *path) { + return basePath(Data, buf, cap, dirs, path); +} + +FILE *configOpen(const char *path, const char *mode) { + const char *abs; + char buf[PATH_MAX]; + const char *dirs = NULL; + while (NULL != (abs = configPath(buf, sizeof(buf), &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; +} + +void dataMkdir(const char *path) { + char buf[PATH_MAX]; + const char *dirs = NULL; + const char *abs = dataPath(buf, sizeof(buf), &dirs, path); + int error = mkdir(abs, S_IRWXU); + if (error && errno != EEXIST) warn("%s", abs); +} + +FILE *dataOpen(const char *path, const char *mode) { + const char *abs; + char buf[PATH_MAX]; + const char *dirs = NULL; + while (NULL != (abs = dataPath(buf, sizeof(buf), &dirs, path))) { + FILE *file = fopen(abs, mode); + if (file) return file; + if (errno != ENOENT) warn("%s", abs); + } + + if (mode[0] != 'r') { + dirs = NULL; + abs = dataPath(buf, sizeof(buf), &dirs, path); + if (!abs) return NULL; + + dataMkdir(""); + FILE *file = fopen(abs, mode); + if (!file) warn("%s", abs); + return file; + } + + FILE *file = fopen(path, mode); + if (!file) warn("%s", path); + return file; +} |