diff options
| author | Matthias Melcher <github@matthiasm.com> | 2024-01-22 20:20:52 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-22 20:20:52 +0100 |
| commit | baa3c6ddc0cf8136ede5fb08acec54675cdc5582 (patch) | |
| tree | 261d4fd30f6b43d9596de4f8c0ca2112c12c8dc3 | |
| parent | 45422a7d1952eb4094c287373afc7f7a1a23ae4a (diff) | |
Fix User Preferences file search order for Unix (#891)
| -rw-r--r-- | src/Fl_Preferences.cxx | 14 | ||||
| -rw-r--r-- | src/drivers/Unix/Fl_Unix_System_Driver.H | 3 | ||||
| -rw-r--r-- | src/drivers/Unix/Fl_Unix_System_Driver.cxx | 163 |
3 files changed, 120 insertions, 60 deletions
diff --git a/src/Fl_Preferences.cxx b/src/Fl_Preferences.cxx index cc3538a78..d4284af19 100644 --- a/src/Fl_Preferences.cxx +++ b/src/Fl_Preferences.cxx @@ -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. \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 \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 @@ -1292,7 +1292,11 @@ Fl_Preferences::RootNode::~RootNode() { // read a preference file and construct the group tree and all entry leaves 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; if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_READ_OK) ) { prefs_->node->clearDirtyFlags(); @@ -1341,7 +1345,11 @@ int Fl_Preferences::RootNode::read() { // write the group tree and all entry leaves 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; if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_WRITE_OK) ) return -1; diff --git a/src/drivers/Unix/Fl_Unix_System_Driver.H b/src/drivers/Unix/Fl_Unix_System_Driver.H index 8bc365515..1859d6583 100644 --- a/src/drivers/Unix/Fl_Unix_System_Driver.H +++ b/src/drivers/Unix/Fl_Unix_System_Driver.H @@ -35,6 +35,9 @@ public: void newUUID(char *uuidBuffer) FL_OVERRIDE; char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor, 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 utf8locale() FL_OVERRIDE; const char *filename_name(const char *buf) FL_OVERRIDE; diff --git a/src/drivers/Unix/Fl_Unix_System_Driver.cxx b/src/drivers/Unix/Fl_Unix_System_Driver.cxx index 1efe01b0a..fc13cbf37 100644 --- a/src/drivers/Unix/Fl_Unix_System_Driver.cxx +++ b/src/drivers/Unix/Fl_Unix_System_Driver.cxx @@ -21,6 +21,7 @@ #include <FL/fl_string_functions.h> // fl_strdup #include <FL/platform.H> #include "../../flstring.h" +#include "../../Fl_String.H" #include "../../Fl_Timeout.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! */ char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, @@ -450,80 +453,126 @@ char *Fl_Unix_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, const char *vendor, const char *application) { - static char *filename = 0L; - if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX); - const char *home = ""; + // Create a static buffer fo our filename + static char *buffer = 0L; + if (!buffer) buffer = (char*)::calloc(1, FL_PATH_MAX); + buffer[0] = '\0'; + + // Make sure that the parameters are not NULL + if ( (vendor==NULL) || (vendor[0]==0) ) + vendor = "unknown"; + if ( (application==NULL) || (application[0]==0) ) + application = "unknown"; + + // Dispatch to the various path creators for the requested root. + char *prefs_path = NULL; int pref_type = root & Fl_Preferences::ROOT_MASK; 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); + prefs_path = preference_user_rootnode(vendor, application, buffer); break; case Fl_Preferences::SYSTEM: - strcpy(filename, "/etc/fltk/"); + prefs_path = preference_system_rootnode(vendor, application, buffer); break; - default: // MEMORY - filename[0] = '\0'; // empty string + case Fl_Preferences::MEMORY: + default: + prefs_path = preference_memory_rootnode(vendor, application, buffer); break; } - // Make sure that the parameters are not NULL - if ( (vendor==NULL) || (vendor[0]==0) ) - vendor = "unknown"; - if ( (application==NULL) || (application[0]==0) ) - application = "unknown"; + return prefs_path; +} - snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename), - "%s/%s.prefs", vendor, application); +/* + Memory based preferences are never saved to any file, so the path is + just and empty string. + */ +char *Fl_Unix_System_Driver::preference_memory_rootnode( + const char * /*vendor*/, + const char * /*application*/, + char *buffer) +{ + 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; +} - // If this is not the USER path (i.e. SYSTEM or MEMORY), we are done - if ((pref_type) != Fl_Preferences::USER) - return filename; +/* + 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. - // If the legacy file exists, we are also done - if (::access(filename, F_OK)==0) - return filename; + For compatibility with 1.3 preferences, this function checks - // This is USER mode, and there is no legacy file. Create an XDG conforming path. - // Check $XDG_CONFIG_HOME, and if it isn't set, default to $HOME/.config - const char *xdg = getenv("XDG_CONFIG_HOME"); - if (xdg==NULL) { - xdg = "~/.config"; + 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; } - filename[0] = 0; - if (strncmp(xdg, "~/", 2)==0) { - 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) { - strlcpy(filename, home, FL_PATH_MAX); - strlcat(filename, "/", FL_PATH_MAX); - strlcat(filename, xdg+6, FL_PATH_MAX); - } else if (strncmp(xdg, "${HOME}/", 8)==0) { - strlcpy(filename, home, FL_PATH_MAX); - strlcat(filename, "/", FL_PATH_MAX); - strlcat(filename, xdg+8, FL_PATH_MAX); + + // 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 { - 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); + } + if (prefs_path_14[prefs_path_14.size()-1]!='/') + prefs_path_14.append('/'); + prefs_path_14.append(vendor); + + // 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; + } } - strlcat(filename, "/", FL_PATH_MAX); - strlcat(filename, vendor, FL_PATH_MAX); - strlcat(filename, "/", FL_PATH_MAX); - strlcat(filename, application, FL_PATH_MAX); - strlcat(filename, ".prefs", FL_PATH_MAX); - return filename; + // 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; } // |
