From 6d776956ec5753c81a73e60cd8dd975a933adf5e Mon Sep 17 00:00:00 2001 From: Mike Sharov Date: Sun, 11 Apr 2021 14:16:53 -0400 Subject: [PATCH] Move configuration and cache to XDG dirs snownews kept configuration in ~/.snownews and the feed cache in ~/.snownews/cache. This commit moves configuration to ~/.config/snownews and the cache to ~/.local/share/snownews, conforming to the XDG basedir specification. snownews will migrate old settings to the new location. --- config.h.in | 4 -- dialog.c | 6 ++- feedio.c | 11 +++--- main.c | 9 +++-- main.h | 1 - netio.c | 5 ++- setup.c | 110 ++++++++++++++++++++++++++++++++++------------------ setup.h | 3 ++ ui.c | 2 +- 9 files changed, 95 insertions(+), 56 deletions(-) diff --git a/config.h.in b/config.h.in index d020399..9987e90 100644 --- a/config.h.in +++ b/config.h.in @@ -29,10 +29,6 @@ // Define to the value of LOCALEPATH environment variable #define LOCALEPATH "@localepath@" -// Snownews configuration files directory, starts with %s for $HOME -#define SNOWNEWS_CONFIG_DIR "%s/.snownews/" -#define SNOWNEWS_CACHE_DIR SNOWNEWS_CONFIG_DIR "cache/" - // Define to output of uname #undef OS diff --git a/dialog.c b/dialog.c index c491f5c..173a362 100644 --- a/dialog.c +++ b/dialog.c @@ -23,6 +23,7 @@ #include "feedio.h" #include "uiutil.h" #include "parse.h" +#include "setup.h" #include char* UIOneLineEntryField (int x, int y) @@ -244,10 +245,11 @@ void FeedInfo (const struct feed* current_feed) free (url); url = NULL; - char cachefile[PATH_MAX]; char* hashme = Hashify (current_feed->feedurl); - snprintf (cachefile, sizeof (cachefile), SNOWNEWS_CACHE_DIR "%s", getenv ("HOME"), hashme); + char cachefile [PATH_MAX]; + CacheFilePath (hashme, cachefile, sizeof(cachefile)); free (hashme); + struct stat cachestat; move (9, centerx - (COLS / 2 - 7)); if (stat (cachefile, &cachestat) < 0) diff --git a/feedio.c b/feedio.c index a2cc90d..85f98c9 100644 --- a/feedio.c +++ b/feedio.c @@ -21,6 +21,7 @@ #include "netio.h" #include "uiutil.h" #include "parse.h" +#include "setup.h" #include #include @@ -99,8 +100,8 @@ int LoadFeed (struct feed* cur_ptr) return 0; char* hashme = Hashify (cur_ptr->feedurl); - char cachefilename[PATH_MAX]; - snprintf (cachefilename, sizeof (cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv ("HOME"), hashme); + char cachefilename [PATH_MAX]; + CacheFilePath (hashme, cachefilename, sizeof(cachefilename)); free (hashme); FILE* cache = fopen (cachefilename, "r"); if (cache == NULL) { @@ -184,7 +185,7 @@ static void WriteFeedUrls (void) // Make a backup of urls. char urlsfilename[PATH_MAX]; - snprintf (urlsfilename, sizeof (urlsfilename), SNOWNEWS_CONFIG_DIR "urls", getenv ("HOME")); + ConfigFilePath ("urls", urlsfilename, sizeof(urlsfilename)); // Write urls FILE* urlfile = fopen (urlsfilename, "w"); @@ -216,8 +217,8 @@ static void WriteFeedUrls (void) static void WriteFeedCache (const struct feed* feed) { char* hashme = Hashify (feed->feedurl); - char cachefilename[PATH_MAX]; - snprintf (cachefilename, sizeof (cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv ("HOME"), hashme); + char cachefilename [PATH_MAX]; + CacheFilePath (hashme, cachefilename, sizeof(cachefilename)); free (hashme); // Check if the feed has been modified since last loaded from this file diff --git a/main.c b/main.c index c1a8064..e3f7194 100644 --- a/main.c +++ b/main.c @@ -96,10 +96,11 @@ enum EPIDAction { pid_file_delete, pid_file_create }; static void make_pidfile_name (char* fnbuf, size_t fnbufsz) { const char* rundir = getenv ("XDG_RUNTIME_DIR"); - if (rundir) - snprintf (fnbuf, fnbufsz, "%s/snownews.pid", rundir); - else - snprintf (fnbuf, fnbufsz, SNOWNEWS_CONFIG_DIR "pid", getenv ("HOME")); + if (!rundir) + rundir = getenv ("TMPDIR"); + if (!rundir) + rundir = "/tmp"; + snprintf (fnbuf, fnbufsz, "%s/snownews.pid", rundir); } static void modifyPIDFile (enum EPIDAction action) diff --git a/main.h b/main.h index 66e9300..05eabc4 100644 --- a/main.h +++ b/main.h @@ -134,7 +134,6 @@ struct settings { struct categories* global_categories; const char* global_charset; char* browser; // Browser command. lynx is standard. - char* useragent; // Snownews User-Agent string. char* proxyname; // Hostname of proxyserver. unsigned short proxyport; // Port on proxyserver to use. struct color color; diff --git a/netio.c b/netio.c index 1a902ad..2ba06cc 100644 --- a/netio.c +++ b/netio.c @@ -17,6 +17,7 @@ #include "netio.h" #include "uiutil.h" +#include "setup.h" #include static size_t FeedReceiver (void* buffer, size_t msz, size_t nm, void* vpfeed) @@ -81,8 +82,8 @@ void DownloadFeed (const char* url, struct feed* fp) // Cookies, if the cookie file is in the config dir char cookiefile [PATH_MAX]; - unsigned cfnl = snprintf (cookiefile, sizeof(cookiefile), SNOWNEWS_CONFIG_DIR "cookies", getenv("HOME")); - if (cfnl < sizeof(cookiefile) && access (cookiefile, R_OK) == 0) + ConfigFilePath ("cookies", cookiefile, sizeof(cookiefile)); + if (access (cookiefile, R_OK) == 0) curl_easy_setopt (curl, CURLOPT_COOKIEFILE, cookiefile); // Save old content diff --git a/setup.c b/setup.c index 3420ae6..cc6f799 100644 --- a/setup.c +++ b/setup.c @@ -23,6 +23,34 @@ #include "conv.h" #include +void ConfigFilePath (const char* filename, char* path, size_t pathsz) +{ + const char* confhome = getenv ("XDG_CONFIG_HOME"); + unsigned pathlen; + if (confhome) + pathlen = snprintf (path, pathsz, "%s/" SNOWNEWS_NAME "/%s", confhome, filename); + else + pathlen = snprintf (path, pathsz, "%s/.config/" SNOWNEWS_NAME "/%s", getenv("HOME"), filename); + if (pathlen >= pathsz) + MainQuit ("ConfigFilePath", "configuration dir path is too long"); + if (path[pathlen-1] == '/') + path[pathlen-1] = 0; +} + +void CacheFilePath (const char* filename, char* path, size_t pathsz) +{ + const char* datahome = getenv ("XDG_DATA_HOME"); + unsigned pathlen; + if (datahome) + pathlen = snprintf (path, pathsz, "%s/" SNOWNEWS_NAME "/%s", datahome, filename); + else + pathlen = snprintf (path, pathsz, "%s/.local/share/" SNOWNEWS_NAME "/%s", getenv("HOME"), filename); + if (pathlen >= pathsz) + MainQuit ("CacheFilePath", "cache dir path is too long"); + if (path[pathlen-1] == '/') + path[pathlen-1] = 0; +} + // Load browser command from ~./snownews/browser. static void SetupBrowser (const char* filename) { @@ -40,7 +68,7 @@ static void SetupBrowser (const char* filename) void SaveBrowserSetting (void) { char browserfilename[PATH_MAX]; - snprintf (browserfilename, sizeof (browserfilename), SNOWNEWS_CONFIG_DIR "browser", getenv ("HOME")); + ConfigFilePath ("browser", browserfilename, sizeof(browserfilename)); FILE* browserfile = fopen (browserfilename, "w"); if (!browserfile) MainQuit (_("Save settings (browser)"), strerror (errno)); @@ -71,24 +99,6 @@ static void SetupProxy (void) free (proxystrbase); } -// Construct the user agent string that snownews sends to the webserver. -// This includes Snownews/VERSION, OS name and language setting. -static void SetupUserAgent (void) -{ - // Constuct the User-Agent string of snownews. This is done here in program init, - // because we need to do it exactly once and it will never change while the program - // is running. - const char* lang = getenv ("LANG"); - if (!lang) - lang = "C"; - // Snonews/VERSION (Linux; de_DE; (http://kiza.kcore.de/software/snownews/) - static const char url[] = "http://snownews.kcore.de/software/snownews/"; - const unsigned urllen = strlen (url); - unsigned ualen = strlen ("Snownews/" SNOWNEWS_VERSION) + 2 + strlen (lang) + 2 + strlen (OS) + 2 + urllen + 2; - _settings.useragent = malloc (ualen); - snprintf (_settings.useragent, ualen, "Snownews/" SNOWNEWS_VERSION " (" OS "; %s; %s)", lang, url); -} - // Define global keybindings and load user customized bindings. static void SetupKeybindings (const char* filename) { @@ -411,6 +421,34 @@ static unsigned SetupFeedList (const char* filename) return numfeeds; } +// Migrates configuration from ~/.snownews to ~/.config/snownews and ~/.local/share/snownews +static void MigrateConfigToXDG (void) +{ + char dirname [PATH_MAX]; + const char* homedir = getenv ("HOME"); + if (!homedir) + MainQuit ("Reading configuration", "you have no home directory"); + + // Migrate cache dir to ~/.local/snownews + snprintf (dirname, sizeof(dirname), "%s/.snownews/cache", homedir); + struct stat dirtest; + if (0 == stat (dirname, &dirtest) && S_ISDIR(dirtest.st_mode)) { + char cachedir [PATH_MAX]; + CacheFilePath ("", cachedir, sizeof(cachedir)); + if (0 != rename (dirname, cachedir)) + MainQuit ("Migrating feed cache ~/.snownews/cache to ~/.local/share/snownews", strerror (errno)); + } + + // Migrate configuration dir to ~/.config/snownews + snprintf (dirname, sizeof(dirname), "%s/.snownews", homedir); + if (0 == stat (dirname, &dirtest) && S_ISDIR(dirtest.st_mode)) { + char configdir [PATH_MAX]; + ConfigFilePath ("", configdir, sizeof(configdir)); + if (0 != rename (dirname, configdir)) + MainQuit ("Migrating configuration ~/.snownews to ~/.config/snownews", strerror (errno)); + } +} + // Create snownews' config directories if they do not exist yet, // load global configuration settings and caches. // @@ -421,43 +459,41 @@ unsigned Config (void) // Set umask to 077 for all created files. umask (0077); + MigrateConfigToXDG(); SetupProxy(); - SetupUserAgent(); // Setup config directories. - char dirname[PATH_MAX]; - snprintf (dirname, sizeof (dirname), SNOWNEWS_CONFIG_DIR, getenv ("HOME")); + char dirname [PATH_MAX]; + ConfigFilePath ("", dirname, sizeof(dirname)); struct stat dirtest; if (0 > stat (dirname, &dirtest)) { - if (0 > mkdir (dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) - MainQuit ("Creating config directory " SNOWNEWS_CONFIG_DIR, strerror (errno)); + if (0 > mkdir (dirname, S_IRWXU)) + MainQuit (dirname, strerror (errno)); } else if (!S_ISDIR (dirtest.st_mode)) - MainQuit ("Creating config directory " SNOWNEWS_CONFIG_DIR, "A file with the name \"" SNOWNEWS_CONFIG_DIR "\" exists!"); + MainQuit (dirname, "is a file"); - snprintf (dirname, sizeof (dirname), SNOWNEWS_CACHE_DIR, getenv ("HOME")); + CacheFilePath ("", dirname, sizeof(dirname)); if (0 > stat (dirname, &dirtest)) { - if (0 > mkdir (dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) - MainQuit (_("Creating config directory " SNOWNEWS_CACHE_DIR), strerror (errno)); + if (0 > mkdir (dirname, S_IRWXU)) + MainQuit (dirname, strerror (errno)); } else if (!S_ISDIR (dirtest.st_mode)) - MainQuit ("Creating config directory " SNOWNEWS_CACHE_DIR, "A file with the name \"" SNOWNEWS_CACHE_DIR "\" exists!"); + MainQuit (dirname, "is a file"); UIStatus (_("Reading configuration settings..."), 0, 0); char filename[PATH_MAX]; - snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "browser", getenv ("HOME")); + ConfigFilePath ("browser", filename, sizeof(filename)); SetupBrowser (filename); - snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "urls", getenv ("HOME")); - unsigned numfeeds = SetupFeedList (filename); - - snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "keybindings", getenv ("HOME")); + ConfigFilePath ("keybindings", filename, sizeof(filename)); SetupKeybindings (filename); - snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "colors", getenv ("HOME")); + ConfigFilePath ("colors", filename, sizeof(filename)); SetupColors (filename); - snprintf (filename, sizeof (filename), SNOWNEWS_CONFIG_DIR "html_entities", getenv ("HOME")); + ConfigFilePath ("html_entities", filename, sizeof(filename)); SetupEntities (filename); - return numfeeds; + ConfigFilePath ("urls", filename, sizeof(filename)); + return SetupFeedList (filename); } diff --git a/setup.h b/setup.h index 634a23d..9015ee1 100644 --- a/setup.h +++ b/setup.h @@ -1,6 +1,7 @@ // This file is part of Snownews - A lightweight console RSS newsreader // // Copyright (c) 2003-2004 Oliver Feiler +// Copyright (c) 2021 Mike Sharov // // Snownews is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License version 3 @@ -19,3 +20,5 @@ unsigned Config (void); void SaveBrowserSetting (void); +void ConfigFilePath (const char* filename, char* path, size_t pathsz); +void CacheFilePath (const char* filename, char* path, size_t pathsz); diff --git a/ui.c b/ui.c index df0238b..e098eb2 100644 --- a/ui.c +++ b/ui.c @@ -1021,7 +1021,7 @@ void UIMainInterface (void) // Remove cachefile from filesystem. char* hashme = Hashify (highlighted->feedurl); char cachefilename[PATH_MAX]; - snprintf (cachefilename, sizeof (cachefilename), SNOWNEWS_CACHE_DIR "%s", getenv ("HOME"), hashme); + CacheFilePath (hashme, cachefilename, sizeof(cachefilename)); free (hashme); // Errors from unlink can be ignored. Worst thing that happens is that