mirror of
https://github.com/GothenburgBitFactory/timewarrior.git
synced 2025-07-07 20:06:39 +02:00
Use XDG Base Directories compliant dirs on Unix for new installations
Signed-off-by: Stanisław Wysocki <garethel@protonmail.com>
This commit is contained in:
parent
598ddb24c8
commit
04822aa195
9 changed files with 230 additions and 86 deletions
|
@ -34,6 +34,7 @@ set (timew_SRCS AtomicFile.cpp AtomicFile.h
|
|||
dom.cpp
|
||||
init.cpp
|
||||
helper.cpp
|
||||
paths.cpp paths.h
|
||||
log.cpp
|
||||
util.cpp
|
||||
validate.cpp)
|
||||
|
|
|
@ -120,7 +120,7 @@ private:
|
|||
void initializeTagDatabase ();
|
||||
|
||||
private:
|
||||
std::string _location {"~/.timewarrior/data"};
|
||||
std::string _location {};
|
||||
std::vector <Datafile> _files {};
|
||||
TagInfoDatabase _tagInfoDatabase {};
|
||||
Journal* _journal {};
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
private:
|
||||
void recordUndoAction (const std::string &, const std::string &, const std::string &);
|
||||
|
||||
std::string _location {"~/.timewarrior/data/undo.data"};
|
||||
std::string _location {};
|
||||
std::shared_ptr <Transaction> _currentTransaction = nullptr;
|
||||
int _size {0};
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <timew.h>
|
||||
#include <iostream>
|
||||
#include <shared.h>
|
||||
#include <paths.h>
|
||||
|
||||
#ifdef HAVE_COMMIT
|
||||
#include <commit.h>
|
||||
|
@ -163,15 +164,15 @@ int CmdDiagnostics (
|
|||
<< (env ? env : "-")
|
||||
<< '\n';
|
||||
|
||||
File cfg (rules.get ("temp.db") + "/timewarrior.cfg");
|
||||
File cfg (paths::configFile ());
|
||||
out << " Cfg: " << describeFile (cfg) << '\n';
|
||||
|
||||
Directory db (rules.get ("temp.db"));
|
||||
Directory db (paths::dbDir ());
|
||||
out << " Database: " << describeFile (db) << '\n';
|
||||
|
||||
for (auto& file : database.files ())
|
||||
{
|
||||
File df (rules.get ("temp.db") + "/data");
|
||||
File df (paths::dbDataDir ());
|
||||
df += file;
|
||||
out << " " << describeFile (df) << '\n';
|
||||
}
|
||||
|
@ -205,8 +206,7 @@ int CmdDiagnostics (
|
|||
out << '\n';
|
||||
|
||||
// Display extensions.
|
||||
Directory extDir (rules.get ("temp.db"));
|
||||
extDir += "extensions";
|
||||
Directory extDir (paths::extensionsDir ());
|
||||
|
||||
out << "Extensions\n"
|
||||
<< " Location: " << describeFile (extDir) << '\n';
|
||||
|
|
|
@ -27,12 +27,11 @@
|
|||
#include <commands.h>
|
||||
#include <Table.h>
|
||||
#include <iostream>
|
||||
#include <paths.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Enumerate all extensions.
|
||||
int CmdExtensions (
|
||||
Rules& rules,
|
||||
const Extensions& extensions)
|
||||
int CmdExtensions (const Extensions& extensions)
|
||||
{
|
||||
Table t;
|
||||
t.width (1024);
|
||||
|
@ -59,8 +58,7 @@ int CmdExtensions (
|
|||
t.set (row, 1, perms);
|
||||
}
|
||||
|
||||
Directory extDir (rules.get ("temp.db"));
|
||||
extDir += "extensions";
|
||||
Directory extDir (paths::extensionsDir ());
|
||||
|
||||
std::cout << '\n'
|
||||
<< "Extensions located in:\n"
|
||||
|
|
|
@ -41,7 +41,7 @@ int CmdDefault ( Rules&, Database&
|
|||
int CmdDelete (const CLI&, Rules&, Database&, Journal& );
|
||||
int CmdDiagnostics ( Rules&, Database&, const Extensions&);
|
||||
int CmdExport (const CLI&, Rules&, Database& );
|
||||
int CmdExtensions ( Rules&, const Extensions&);
|
||||
int CmdExtensions ( const Extensions&);
|
||||
int CmdFill (const CLI&, Rules&, Database&, Journal& );
|
||||
int CmdGaps (const CLI&, Rules&, Database& );
|
||||
int CmdGet (const CLI&, Rules&, Database& );
|
||||
|
|
80
src/init.cpp
80
src/init.cpp
|
@ -32,6 +32,7 @@
|
|||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <paths.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool lightweightVersionCheck (int argc, const char** argv)
|
||||
|
@ -151,74 +152,7 @@ void initializeDataJournalAndRules (
|
|||
}
|
||||
|
||||
enableDebugMode (rules.getBoolean ("debug"));
|
||||
|
||||
// The $TIMEWARRIORDB environment variable overrides the default value of ~/.timewarrior
|
||||
Directory dbLocation;
|
||||
char* override = getenv ("TIMEWARRIORDB");
|
||||
dbLocation = Directory (override ? override : "~/.timewarrior");
|
||||
|
||||
// If dbLocation exists, but is not readable/writable/executable, error.
|
||||
if (dbLocation.exists () &&
|
||||
(! dbLocation.readable () ||
|
||||
! dbLocation.writable () ||
|
||||
! dbLocation.executable ()))
|
||||
{
|
||||
throw format ("Database is not readable at '{1}'", dbLocation._data);
|
||||
}
|
||||
|
||||
// If dbLocation does not exist, ask whether it should be created.
|
||||
bool shinyNewDatabase = false;
|
||||
|
||||
if (! dbLocation.exists ())
|
||||
{
|
||||
if ((cli.getHint ("yes", false) || confirm ("Create new database in " + dbLocation._data + "?")))
|
||||
{
|
||||
dbLocation.create (0700);
|
||||
shinyNewDatabase = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::string("Initial setup aborted by user");
|
||||
}
|
||||
}
|
||||
|
||||
// Create extensions subdirectory if necessary.
|
||||
Directory extensions (dbLocation);
|
||||
extensions += "extensions";
|
||||
|
||||
if (! extensions.exists ())
|
||||
{
|
||||
extensions.create (0700);
|
||||
}
|
||||
|
||||
// Create data subdirectory if necessary.
|
||||
Directory data (dbLocation);
|
||||
data += "data";
|
||||
|
||||
if (! data.exists ())
|
||||
{
|
||||
data.create (0700);
|
||||
}
|
||||
|
||||
// Load the configuration data.
|
||||
Path configFile (dbLocation);
|
||||
configFile += "timewarrior.cfg";
|
||||
|
||||
if (! configFile.exists ())
|
||||
{
|
||||
File (configFile).create (0600);
|
||||
}
|
||||
|
||||
rules.load (configFile._data);
|
||||
|
||||
// This value is not written out to disk, as there would be no point.
|
||||
// Having located the config file, the 'db' location is already known.
|
||||
// This is just for subsequent internal use.
|
||||
rules.set ("temp.db", dbLocation._data);
|
||||
|
||||
// Perhaps some subsequent code would like to know this is a new db and possibly a first run.
|
||||
if (shinyNewDatabase)
|
||||
rules.set ("temp.shiny", 1);
|
||||
paths::initializeDirs (cli, rules);
|
||||
|
||||
if (rules.has ("debug.indicator"))
|
||||
setDebugIndicator (rules.get ("debug.indicator"));
|
||||
|
@ -236,9 +170,10 @@ void initializeDataJournalAndRules (
|
|||
}
|
||||
}
|
||||
|
||||
journal.initialize (data._data + "/undo.data", rules.getInteger ("journal.size"));
|
||||
std::string dbDataDir = paths::dbDataDir ();
|
||||
journal.initialize (dbDataDir + "/undo.data", rules.getInteger ("journal.size"));
|
||||
// Initialize the database (no data read), but files are enumerated.
|
||||
database.initialize (data._data, journal);
|
||||
database.initialize (dbDataDir, journal);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -247,8 +182,7 @@ void initializeExtensions (
|
|||
const Rules& rules,
|
||||
Extensions& extensions)
|
||||
{
|
||||
Directory extDir (rules.get ("temp.db"));
|
||||
extDir += "extensions";
|
||||
Directory extDir (paths::extensionsDir ());
|
||||
|
||||
extensions.initialize (extDir._data);
|
||||
|
||||
|
@ -289,7 +223,7 @@ int dispatchCommand (
|
|||
else if (command == "delete") status = CmdDelete (cli, rules, database, journal );
|
||||
else if (command == "diagnostics") status = CmdDiagnostics ( rules, database, extensions);
|
||||
else if (command == "export") status = CmdExport (cli, rules, database );
|
||||
else if (command == "extensions") status = CmdExtensions ( rules, extensions);
|
||||
else if (command == "extensions") status = CmdExtensions ( extensions);
|
||||
/*
|
||||
else if (command == "fill") status = CmdFill (cli, rules, database, journal );
|
||||
//*/
|
||||
|
|
169
src/paths.cpp
Normal file
169
src/paths.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
#include <FS.h>
|
||||
#include <format.h>
|
||||
#include <paths.h>
|
||||
#include <shared.h>
|
||||
|
||||
namespace paths
|
||||
{
|
||||
static const char *legacy_config_dir = "~/.timewarrior";
|
||||
static const bool uses_legacy_config = Directory (legacy_config_dir).exists ();
|
||||
const char *timewarriordb = getenv ("TIMEWARRIORDB");
|
||||
|
||||
#ifdef __unix__
|
||||
std::string getPath (const char *xdg_path)
|
||||
{
|
||||
if (timewarriordb != nullptr)
|
||||
{
|
||||
return timewarriordb;
|
||||
}
|
||||
else if (uses_legacy_config)
|
||||
{
|
||||
return legacy_config_dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string (xdg_path) + "/timewarrior";
|
||||
}
|
||||
}
|
||||
|
||||
const char *getenv_default (const char *env, const char *default_value)
|
||||
{
|
||||
const char *value = getenv (env);
|
||||
if (value == nullptr)
|
||||
{
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string conf_dir = getPath (getenv_default ("XDG_CONFIG_HOME", "~/.config"));
|
||||
static std::string data_dir = getPath (getenv_default ("XDG_DATA_HOME", "~/.local/share"));
|
||||
std::string configDir () { return conf_dir; }
|
||||
std::string dbDir () { return data_dir; }
|
||||
|
||||
#else
|
||||
std::string getPath ()
|
||||
{
|
||||
if (timewarriordb != nullptr)
|
||||
{
|
||||
return timewarriordb;
|
||||
}
|
||||
else
|
||||
{
|
||||
return legacy_config_dir;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string path = getPath ();
|
||||
|
||||
std::string configDir ()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string dbDir ()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::string configFile () { return configDir () + "/timewarrior.cfg"; }
|
||||
std::string dbDataDir () { return dbDir () + "/data"; }
|
||||
std::string extensionsDir () { return configDir () + "/extensions"; }
|
||||
|
||||
void initializeDirs (const CLI &cli, Rules &rules)
|
||||
{
|
||||
Directory configLocation = Directory (configDir ());
|
||||
bool configDirExists = configLocation.exists ();
|
||||
|
||||
if (configDirExists &&
|
||||
(!configLocation.readable () ||
|
||||
!configLocation.writable () ||
|
||||
!configLocation.executable ()))
|
||||
{
|
||||
throw format ("Config is not readable at '{1}'", configLocation._data);
|
||||
}
|
||||
|
||||
Directory dbLocation = Directory (dbDir ());
|
||||
bool dataLocationExists = dbLocation.exists ();
|
||||
if (dataLocationExists &&
|
||||
(!dbLocation.readable () ||
|
||||
!dbLocation.writable () ||
|
||||
!dbLocation.executable ()))
|
||||
{
|
||||
throw format ("Database is not readable at '{1}'", dbLocation._data);
|
||||
}
|
||||
|
||||
std::string question = "";
|
||||
if (!configDirExists)
|
||||
{
|
||||
question += "Create new config in " + configLocation._data + "?";
|
||||
}
|
||||
if (!dataLocationExists && configLocation._data != dbLocation._data)
|
||||
{
|
||||
if (question != "") {
|
||||
question += "\n";
|
||||
}
|
||||
question += "Create new database in " + dbLocation._data + "?";
|
||||
}
|
||||
|
||||
if (!configDirExists || !dataLocationExists)
|
||||
{
|
||||
if (cli.getHint ("yes", false) || confirm (question))
|
||||
{
|
||||
if (!configDirExists)
|
||||
{
|
||||
configLocation.create (0700);
|
||||
}
|
||||
if (!dataLocationExists)
|
||||
{
|
||||
dbLocation.create (0700);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::string("Initial setup aborted by user");
|
||||
}
|
||||
}
|
||||
|
||||
// Create extensions subdirectory if necessary.
|
||||
Directory extensionsLocation (extensionsDir ());
|
||||
|
||||
if (!extensionsLocation.exists ())
|
||||
{
|
||||
extensionsLocation.create (0700);
|
||||
}
|
||||
|
||||
// Create data subdirectory if necessary.
|
||||
Directory dbDataLocation (dbDataDir ());
|
||||
|
||||
if (!dbDataLocation.exists ())
|
||||
{
|
||||
dbDataLocation.create (0700);
|
||||
}
|
||||
|
||||
Path configFileLocation (configFile ());
|
||||
|
||||
if (!configFileLocation.exists ())
|
||||
{
|
||||
File (configFileLocation).create (0600);
|
||||
}
|
||||
|
||||
// Load the configuration data.
|
||||
rules.load (configFileLocation);
|
||||
|
||||
// This value is not written out to disk, as there would be no point.
|
||||
// Having located the config file, the 'db' location is already known.
|
||||
// This is just for subsequent internal use.
|
||||
rules.set ("temp.db", dbLocation);
|
||||
rules.set ("temp.extensions", extensionsLocation);
|
||||
rules.set ("temp.config", configFileLocation);
|
||||
|
||||
// Perhaps some subsequent code would like to know this is a new db and possibly a first run.
|
||||
if (!dataLocationExists)
|
||||
rules.set ("temp.shiny", 1);
|
||||
}
|
||||
|
||||
} // namespace paths
|
42
src/paths.h
Normal file
42
src/paths.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 - 2021, Thomas Lauf, Paul Beckingham, Federico Hernandez.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// https://www.opensource.org/licenses/mit-license.php
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDED_PATH_RESOLVER
|
||||
#define INCLUDED_PATH_RESOLVER
|
||||
#include <string>
|
||||
#include <Rules.h>
|
||||
#include <CLI.h>
|
||||
#include <Rules.h>
|
||||
|
||||
namespace paths {
|
||||
void initializeDirs (const CLI&, Rules&);
|
||||
std::string configDir ();
|
||||
std::string configFile ();
|
||||
std::string dbDir ();
|
||||
std::string dbDataDir ();
|
||||
std::string extensionsDir ();
|
||||
}
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue