mirror of
https://github.com/fltk/fltk.git
synced 2026-05-30 13:05:35 +08:00
Fix User Preferences file search order for Unix (#891)
This commit is contained in:
+11
-3
@@ -236,7 +236,7 @@ Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size,
|
|||||||
which would silently fail to create a preference file.
|
which would silently fail to create a preference file.
|
||||||
|
|
||||||
\param[in] root can be \c USER_L or \c SYSTEM_L for user specific or system
|
\param[in] root can be \c USER_L or \c SYSTEM_L for user specific or system
|
||||||
wide preferences, add the CLEAR flag to start with a clean set of
|
wide preferences, add the \c CLEAR flag to start with a clean set of
|
||||||
preferences instead of reading them from a preexisting database
|
preferences instead of reading them from a preexisting database
|
||||||
\param[in] vendor unique text describing the company or author of this file, must be a valid filepath segment
|
\param[in] vendor unique text describing the company or author of this file, must be a valid filepath segment
|
||||||
\param[in] application unique text describing the application, must be a valid filepath segment
|
\param[in] application unique text describing the application, must be a valid filepath segment
|
||||||
@@ -1292,7 +1292,11 @@ Fl_Preferences::RootNode::~RootNode() {
|
|||||||
|
|
||||||
// read a preference file and construct the group tree and all entry leaves
|
// read a preference file and construct the group tree and all entry leaves
|
||||||
int Fl_Preferences::RootNode::read() {
|
int Fl_Preferences::RootNode::read() {
|
||||||
if (!filename_) // RUNTIME preferences, or filename could not be created
|
if ( (root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::MEMORY ) {
|
||||||
|
prefs_->node->clearDirtyFlags();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!filename_ || !filename_[0]) // filename could not be created
|
||||||
return -1;
|
return -1;
|
||||||
if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_READ_OK) ) {
|
if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_READ_OK) ) {
|
||||||
prefs_->node->clearDirtyFlags();
|
prefs_->node->clearDirtyFlags();
|
||||||
@@ -1341,7 +1345,11 @@ int Fl_Preferences::RootNode::read() {
|
|||||||
|
|
||||||
// write the group tree and all entry leaves
|
// write the group tree and all entry leaves
|
||||||
int Fl_Preferences::RootNode::write() {
|
int Fl_Preferences::RootNode::write() {
|
||||||
if (!filename_) // RUNTIME preferences, or filename could not be created
|
if ( (root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::MEMORY ) {
|
||||||
|
prefs_->node->clearDirtyFlags();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!filename_ || !filename_[0]) // filename could not be created
|
||||||
return -1;
|
return -1;
|
||||||
if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_WRITE_OK) )
|
if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_WRITE_OK) )
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ public:
|
|||||||
void newUUID(char *uuidBuffer) FL_OVERRIDE;
|
void newUUID(char *uuidBuffer) FL_OVERRIDE;
|
||||||
char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
|
char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
|
||||||
const char *application) FL_OVERRIDE;
|
const char *application) FL_OVERRIDE;
|
||||||
|
char *preference_memory_rootnode(const char *vendor, const char *application, char *buffer);
|
||||||
|
char *preference_system_rootnode(const char *vendor, const char *application, char *buffer);
|
||||||
|
char *preference_user_rootnode(const char *vendor, const char *application, char *buffer);
|
||||||
int preferences_need_protection_check() FL_OVERRIDE {return 1;}
|
int preferences_need_protection_check() FL_OVERRIDE {return 1;}
|
||||||
int utf8locale() FL_OVERRIDE;
|
int utf8locale() FL_OVERRIDE;
|
||||||
const char *filename_name(const char *buf) FL_OVERRIDE;
|
const char *filename_name(const char *buf) FL_OVERRIDE;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <FL/fl_string_functions.h> // fl_strdup
|
#include <FL/fl_string_functions.h> // fl_strdup
|
||||||
#include <FL/platform.H>
|
#include <FL/platform.H>
|
||||||
#include "../../flstring.h"
|
#include "../../flstring.h"
|
||||||
|
#include "../../Fl_String.H"
|
||||||
#include "../../Fl_Timeout.h"
|
#include "../../Fl_Timeout.h"
|
||||||
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
@@ -443,6 +444,8 @@ void Fl_Unix_System_Driver::newUUID(char *uuidBuffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Create a buffer that holds the absolute file path and name of the preferences
|
||||||
|
file for the given root, vendor, and application name.
|
||||||
Note: `prefs` can be NULL!
|
Note: `prefs` can be NULL!
|
||||||
*/
|
*/
|
||||||
char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/,
|
char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/,
|
||||||
@@ -450,33 +453,10 @@ char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/,
|
|||||||
const char *vendor,
|
const char *vendor,
|
||||||
const char *application)
|
const char *application)
|
||||||
{
|
{
|
||||||
static char *filename = 0L;
|
// Create a static buffer fo our filename
|
||||||
if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX);
|
static char *buffer = 0L;
|
||||||
const char *home = "";
|
if (!buffer) buffer = (char*)::calloc(1, FL_PATH_MAX);
|
||||||
int pref_type = root & Fl_Preferences::ROOT_MASK;
|
buffer[0] = '\0';
|
||||||
switch (pref_type) {
|
|
||||||
case Fl_Preferences::USER:
|
|
||||||
home = getenv("HOME");
|
|
||||||
// make sure that $HOME is set to an existing directory
|
|
||||||
if ((home == NULL) || (home[0] == 0) || (::access(home, F_OK) == -1)) {
|
|
||||||
struct passwd *pw = getpwuid(getuid());
|
|
||||||
if (pw)
|
|
||||||
home = pw->pw_dir;
|
|
||||||
}
|
|
||||||
if ((home == 0L) || (home[0] == 0) || (::access(home, F_OK) == -1))
|
|
||||||
return NULL;
|
|
||||||
strlcpy(filename, home, FL_PATH_MAX);
|
|
||||||
if (filename[strlen(filename) - 1] != '/')
|
|
||||||
strlcat(filename, "/", FL_PATH_MAX);
|
|
||||||
strlcat(filename, ".fltk/", FL_PATH_MAX);
|
|
||||||
break;
|
|
||||||
case Fl_Preferences::SYSTEM:
|
|
||||||
strcpy(filename, "/etc/fltk/");
|
|
||||||
break;
|
|
||||||
default: // MEMORY
|
|
||||||
filename[0] = '\0'; // empty string
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the parameters are not NULL
|
// Make sure that the parameters are not NULL
|
||||||
if ( (vendor==NULL) || (vendor[0]==0) )
|
if ( (vendor==NULL) || (vendor[0]==0) )
|
||||||
@@ -484,46 +464,115 @@ char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/,
|
|||||||
if ( (application==NULL) || (application[0]==0) )
|
if ( (application==NULL) || (application[0]==0) )
|
||||||
application = "unknown";
|
application = "unknown";
|
||||||
|
|
||||||
snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename),
|
// Dispatch to the various path creators for the requested root.
|
||||||
"%s/%s.prefs", vendor, application);
|
char *prefs_path = NULL;
|
||||||
|
int pref_type = root & Fl_Preferences::ROOT_MASK;
|
||||||
// If this is not the USER path (i.e. SYSTEM or MEMORY), we are done
|
switch (pref_type) {
|
||||||
if ((pref_type) != Fl_Preferences::USER)
|
case Fl_Preferences::USER:
|
||||||
return filename;
|
prefs_path = preference_user_rootnode(vendor, application, buffer);
|
||||||
|
break;
|
||||||
// If the legacy file exists, we are also done
|
case Fl_Preferences::SYSTEM:
|
||||||
if (::access(filename, F_OK)==0)
|
prefs_path = preference_system_rootnode(vendor, application, buffer);
|
||||||
return filename;
|
break;
|
||||||
|
case Fl_Preferences::MEMORY:
|
||||||
// This is USER mode, and there is no legacy file. Create an XDG conforming path.
|
default:
|
||||||
// Check $XDG_CONFIG_HOME, and if it isn't set, default to $HOME/.config
|
prefs_path = preference_memory_rootnode(vendor, application, buffer);
|
||||||
const char *xdg = getenv("XDG_CONFIG_HOME");
|
break;
|
||||||
if (xdg==NULL) {
|
|
||||||
xdg = "~/.config";
|
|
||||||
}
|
}
|
||||||
filename[0] = 0;
|
|
||||||
if (strncmp(xdg, "~/", 2)==0) {
|
return prefs_path;
|
||||||
strlcpy(filename, home, FL_PATH_MAX);
|
}
|
||||||
strlcat(filename, "/", FL_PATH_MAX);
|
|
||||||
strlcat(filename, xdg+2, FL_PATH_MAX);
|
/*
|
||||||
} else if (strncmp(xdg, "$HOME/", 6)==0) {
|
Memory based preferences are never saved to any file, so the path is
|
||||||
strlcpy(filename, home, FL_PATH_MAX);
|
just and empty string.
|
||||||
strlcat(filename, "/", FL_PATH_MAX);
|
*/
|
||||||
strlcat(filename, xdg+6, FL_PATH_MAX);
|
char *Fl_Unix_System_Driver::preference_memory_rootnode(
|
||||||
} else if (strncmp(xdg, "${HOME}/", 8)==0) {
|
const char * /*vendor*/,
|
||||||
strlcpy(filename, home, FL_PATH_MAX);
|
const char * /*application*/,
|
||||||
strlcat(filename, "/", FL_PATH_MAX);
|
char *buffer)
|
||||||
strlcat(filename, xdg+8, FL_PATH_MAX);
|
{
|
||||||
|
buffer[0] = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The path and file name for system preferences on Unix type
|
||||||
|
systems is `/etc/fltk/{vendor}/{application}.prefs`.
|
||||||
|
*/
|
||||||
|
char *Fl_Unix_System_Driver::preference_system_rootnode(
|
||||||
|
const char *vendor,
|
||||||
|
const char *application,
|
||||||
|
char *buffer)
|
||||||
|
{
|
||||||
|
snprintf(buffer, FL_PATH_MAX, "/etc/fltk/%s/%s.prefs", vendor, application);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The user path changed between FLTK 1.3 and 1.4. It is now calculated
|
||||||
|
using XDG guidelines. It is `$XDG_CONFIG_HOME/{vendor}/{application}.prefs`
|
||||||
|
if `$XDG_CONFIG_HOME` is set, and `$HOME/.config/{vendor}/{application}.prefs`
|
||||||
|
if `$XDG_CONFIG_HOME` is not set or empty.
|
||||||
|
|
||||||
|
For compatibility with 1.3 preferences, this function checks
|
||||||
|
|
||||||
|
1. Does the XDG based top level folder '{vendor}' exist? If yes, use it.
|
||||||
|
2. If not: does the old location $HOME/.fltk/{vendor} exist? If yes, use it.
|
||||||
|
3. If neither: fall back to (1.) and use the XDG based folder (create it and
|
||||||
|
the prefs file below it).
|
||||||
|
*/
|
||||||
|
char *Fl_Unix_System_Driver::preference_user_rootnode(
|
||||||
|
const char *vendor,
|
||||||
|
const char *application,
|
||||||
|
char *buffer)
|
||||||
|
{
|
||||||
|
// Find the path to the user's home directory.
|
||||||
|
Fl_String home_path = getenv("HOME");
|
||||||
|
if (home_path.empty()) {
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
if (pw)
|
||||||
|
home_path = pw->pw_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1: Generate the 1.4 path for this vendor and application.
|
||||||
|
Fl_String prefs_path_14 = getenv("XDG_CONFIG_HOME");
|
||||||
|
if (prefs_path_14.empty()) {
|
||||||
|
prefs_path_14 = home_path + "/.config";
|
||||||
} else {
|
} else {
|
||||||
strlcpy(filename, xdg, FL_PATH_MAX);
|
if (prefs_path_14[prefs_path_14.size()-1]!='/')
|
||||||
|
prefs_path_14.append('/');
|
||||||
|
if (prefs_path_14.find("~/")==0) // starts with "~"
|
||||||
|
prefs_path_14.replace(0, 1, home_path);
|
||||||
|
size_t h_env = prefs_path_14.find("${HOME}");
|
||||||
|
if (h_env!=prefs_path_14.npos)
|
||||||
|
prefs_path_14.replace(h_env, 7, home_path);
|
||||||
|
h_env = prefs_path_14.find("$HOME/");
|
||||||
|
if (h_env!=prefs_path_14.npos)
|
||||||
|
prefs_path_14.replace(h_env, 5, home_path);
|
||||||
}
|
}
|
||||||
strlcat(filename, "/", FL_PATH_MAX);
|
if (prefs_path_14[prefs_path_14.size()-1]!='/')
|
||||||
strlcat(filename, vendor, FL_PATH_MAX);
|
prefs_path_14.append('/');
|
||||||
strlcat(filename, "/", FL_PATH_MAX);
|
prefs_path_14.append(vendor);
|
||||||
strlcat(filename, application, FL_PATH_MAX);
|
|
||||||
strlcat(filename, ".prefs", FL_PATH_MAX);
|
|
||||||
|
|
||||||
return filename;
|
// 2: If this base path does not exist, try the 1.3 path
|
||||||
|
if (::access(prefs_path_14.c_str(), F_OK) == -1) {
|
||||||
|
Fl_String prefs_path_13 = home_path + ".fltk/" + vendor;
|
||||||
|
if (::access(prefs_path_14.c_str(), F_OK) == 0) {
|
||||||
|
prefs_path_13.append('/');
|
||||||
|
prefs_path_13.append(application);
|
||||||
|
prefs_path_13.append(".prefs");
|
||||||
|
strlcpy(buffer, prefs_path_13.c_str(), FL_PATH_MAX);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3: neither path exists, return the 1.4 file path and name
|
||||||
|
prefs_path_14.append('/');
|
||||||
|
prefs_path_14.append(application);
|
||||||
|
prefs_path_14.append(".prefs");
|
||||||
|
strlcpy(buffer, prefs_path_14.c_str(), FL_PATH_MAX);
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user