Permalink
Browse files

Fall back to checking XDG locations for config files.

If --config wasn't passed and $HOME/.xsettingsd doesn't
exist, fall back to the locations suggested by the XDG Base
Directory Specification (http://goo.gl/8AX6Lb). Add tests
for the new code.

(This was requested by Krytarik Raido and Unit 193.)
  • Loading branch information...
derat committed Jun 15, 2015
1 parent 37e831c commit b4999f5e9e99224caf97d09f25ee731774ecd7be
Showing with 169 additions and 12 deletions.
  1. +62 −2 common.cc
  2. +12 −0 common.h
  3. +66 −0 common_test.cc
  4. +29 −10 xsettingsd.cc
View
@@ -5,18 +5,44 @@
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
using std::string;
using std::vector;
namespace xsettingsd {
std::string StringPrintf(const char* format, ...) {
string StringPrintf(const char* format, ...) {
char buffer[1024];
va_list argp;
va_start(argp, format);
vsnprintf(buffer, sizeof(buffer), format, argp);
va_end(argp);
return std::string(buffer);
return string(buffer);
}
vector<string> SplitString(const string& str, const string& delim) {
if (str.empty())
return vector<string>();
if (delim.empty())
return vector<string>(1, str);
vector<string> parts;
size_t start = 0;
while (start <= str.size()) {
if (start == str.size()) {
parts.push_back(string());
break;
}
size_t next = str.find(delim, start);
if (next == string::npos) {
parts.push_back(str.substr(start, str.size() - start));
break;
}
parts.push_back(str.substr(start, next - start));
start = next + delim.size();
}
return parts;
}
bool IsLittleEndian() {
@@ -33,6 +59,40 @@ int GetPadding(int length, int increment) {
//return ((n + m - 1) & (~(m - 1)));
}
vector<string> GetDefaultConfigFilePaths() {
vector<string> paths;
// Try ~/.xsettingsd first.
const char* home_dir = getenv("HOME");
if (home_dir)
paths.push_back(StringPrintf("%s/.xsettingsd", home_dir));
// Next look under $XDG_CONFIG_HOME, or in $HOME/.config if $XDG_CONFIG_HOME
// is unset.
vector<string> xdg_dirs;
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
if (xdg_config_home && xdg_config_home[0] != '\0')
xdg_dirs.push_back(xdg_config_home);
else if (home_dir)
xdg_dirs.push_back(StringPrintf("%s/.config", home_dir));
// Finally split the colon-delimited $XDG_CONFIG_DIRS variable.
const char* xdg_config_dirs = getenv("XDG_CONFIG_DIRS");
if (xdg_config_dirs) {
vector<string> split_dirs = SplitString(xdg_config_dirs, ":");
xdg_dirs.insert(xdg_dirs.end(), split_dirs.begin(), split_dirs.end());
} else {
xdg_dirs.push_back("/etc");
}
for (size_t i = 0; i < xdg_dirs.size(); ++i) {
paths.push_back(StringPrintf("%s/xsettingsd/xsettingsd.conf",
xdg_dirs[i].c_str()));
}
return paths;
}
const char* kProgName = "xsettingsd";
} // namespace xsettingsd
View
@@ -5,6 +5,7 @@
#define __XSETTINGSD_COMMON_H__
#include <string>
#include <vector>
namespace xsettingsd {
@@ -14,10 +15,21 @@ namespace xsettingsd {
std::string StringPrintf(const char* format, ...);
// Splits |str| along |delim|. Repeated occurrences of |delim| in |str| will
// result in empty strings in the output. An empty |delim| will result in |str|
// being returned unsplit. An empty |str| will result in an empty vector.
std::vector<std::string> SplitString(const std::string& str,
const std::string& delim);
bool IsLittleEndian();
int GetPadding(int length, int increment);
// Returns $HOME/.xsettingsd followed by all of the config file locations
// specified by the XDG Base Directory Specification
// (http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html).
std::vector<std::string> GetDefaultConfigFilePaths();
extern const char* kProgName;
} // namespace xsettingsd
View
@@ -1,11 +1,45 @@
// Copyright 2009 Daniel Erat <dan@erat.org>
// All rights reserved.
#include <cstdlib>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "common.h"
using std::string;
using std::vector;
namespace xsettingsd {
namespace {
// Returns |parts| joined by '|'.
string Join(const vector<string>& parts) {
string out;
for (size_t i = 0; i < parts.size(); ++i)
out += (i > 0 ? "|" : "") + parts[i];
return out;
}
} // namespace
TEST(CommonTest, SplitString) {
EXPECT_EQ("a|b|c", Join(SplitString("a,b,c", ",")));
EXPECT_EQ("a|b|", Join(SplitString("a,b,", ",")));
EXPECT_EQ("|a|b", Join(SplitString(",a,b", ",")));
EXPECT_EQ("|a|b|", Join(SplitString(",a,b,", ",")));
EXPECT_EQ("a||b", Join(SplitString("a,,b", ",")));
EXPECT_EQ("|", Join(SplitString(",", ",")));
EXPECT_EQ("foo", Join(SplitString("foo", ",")));
EXPECT_EQ("foo|bar", Join(SplitString("fooabcbar", "abc")));
EXPECT_EQ("|foo", Join(SplitString("abcfoo", "abc")));
EXPECT_EQ("foo|", Join(SplitString("fooabc", "abc")));
EXPECT_EQ(0, SplitString("", ",").size());
EXPECT_EQ(0, SplitString("", "").size());
EXPECT_EQ("abc", Join(SplitString("abc", "")));
}
TEST(CommonTest, GetPadding) {
EXPECT_EQ(0, GetPadding(0, 4));
@@ -19,6 +53,38 @@ TEST(CommonTest, GetPadding) {
EXPECT_EQ(0, GetPadding(8, 4));
}
TEST(CommonTest, GetDefaultConfigFilePath) {
// With $HOME missing and none of the XDG vars, we should just use /etc.
ASSERT_EQ(0, unsetenv("HOME"));
ASSERT_EQ(0, unsetenv("XDG_CONFIG_HOME"));
ASSERT_EQ(0, unsetenv("XDG_CONFIG_DIRS"));
EXPECT_EQ("/etc/xsettingsd/xsettingsd.conf",
Join(GetDefaultConfigFilePaths()));
// Now set $HOME. It should be searched first, followed by the default XDG
// paths.
ASSERT_EQ(0, setenv("HOME", "/home/user", 1 /* overwrite */));
EXPECT_EQ("/home/user/.xsettingsd|"
"/home/user/.config/xsettingsd/xsettingsd.conf|"
"/etc/xsettingsd/xsettingsd.conf",
Join(GetDefaultConfigFilePaths()));
// Use a custom $XDG_CONFIG_HOME.
ASSERT_EQ(0, setenv("XDG_CONFIG_HOME", "/home/user/.myconf", 1));
EXPECT_EQ("/home/user/.xsettingsd|"
"/home/user/.myconf/xsettingsd/xsettingsd.conf|"
"/etc/xsettingsd/xsettingsd.conf",
Join(GetDefaultConfigFilePaths()));
// Now put a few paths in $XDG_CONFIG_DIRS.
ASSERT_EQ(0, setenv("XDG_CONFIG_DIRS", "/etc2:/etc3", 1));
EXPECT_EQ("/home/user/.xsettingsd|"
"/home/user/.myconf/xsettingsd/xsettingsd.conf|"
"/etc2/xsettingsd/xsettingsd.conf|"
"/etc3/xsettingsd/xsettingsd.conf",
Join(GetDefaultConfigFilePaths()));
}
} // namespace xsettingsd
int main(int argc, char** argv) {
View
@@ -1,25 +1,41 @@
// Copyright 2009 Daniel Erat <dan@erat.org>
// All rights reserved.
#include <getopt.h>
#include <unistd.h>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <getopt.h>
#include <string>
#include <unistd.h>
#include "common.h"
#include "config_parser.h"
#include "settings_manager.h"
using namespace xsettingsd;
using std::string;
using std::vector;
namespace {
void HandleSignal(int signum) {
}
// Returns the first path in |paths| that is readable, or an empty string if
// none of the paths can be read.
string GetFirstReadablePath(const vector<string>& paths) {
for (size_t i = 0; i < paths.size(); ++i) {
if (access(paths[i].c_str(), R_OK) == 0) {
return paths[i];
}
}
return string();
}
} // namespace
int main(int argc, char** argv) {
static const char* kUsage =
"Usage: xsettingsd [OPTION] ...\n"
@@ -32,7 +48,7 @@ int main(int argc, char** argv) {
" -s, --screen=SCREEN screen to use (default is all)\n";
int screen = -1;
string config_file = "";
string config_file;
struct option options[] = {
{ "config", 1, NULL, 'c', },
@@ -61,17 +77,20 @@ int main(int argc, char** argv) {
}
}
// Use the default config file if one wasn't supplied via a flag.
// Check default config file locations if one wasn't supplied via a flag.
if (config_file.empty()) {
const char* home_dir = getenv("HOME");
if (!home_dir) {
fprintf(stderr, "%s: $HOME undefined; use --config=FILE\n", kProgName);
const vector<string> paths = xsettingsd::GetDefaultConfigFilePaths();
config_file = GetFirstReadablePath(paths);
if (config_file.empty()) {
fprintf(stderr, "%s: Couldn't find config file. Tried the following:\n",
xsettingsd::kProgName);
for (size_t i = 0; i < paths.size(); ++i)
fprintf(stderr, " %s\n", paths[i].c_str());
return 1;
}
config_file = StringPrintf("%s/.xsettingsd", home_dir);
}
SettingsManager manager(config_file);
xsettingsd::SettingsManager manager(config_file);
if (!manager.LoadConfig())
return 1;
if (!manager.InitX11(screen, true))

0 comments on commit b4999f5

Please sign in to comment.