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.
This commit is contained in:
Mike Sharov 2021-04-11 14:16:53 -04:00
parent 5604d30c00
commit 6d776956ec
9 changed files with 95 additions and 56 deletions

View File

@ -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

View File

@ -23,6 +23,7 @@
#include "feedio.h"
#include "uiutil.h"
#include "parse.h"
#include "setup.h"
#include <ncurses.h>
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)

View File

@ -21,6 +21,7 @@
#include "netio.h"
#include "uiutil.h"
#include "parse.h"
#include "setup.h"
#include <ncurses.h>
#include <libxml/parser.h>
@ -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

9
main.c
View File

@ -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)

1
main.h
View File

@ -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;

View File

@ -17,6 +17,7 @@
#include "netio.h"
#include "uiutil.h"
#include "setup.h"
#include <curl/curl.h>
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

110
setup.c
View File

@ -23,6 +23,34 @@
#include "conv.h"
#include <ncurses.h>
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);
}

View File

@ -1,6 +1,7 @@
// This file is part of Snownews - A lightweight console RSS newsreader
//
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
//
// 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);

2
ui.c
View File

@ -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