mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-18 20:10:29 +01:00
As reported in Debian bug #922284 [1]: As evident from the prefix, GNUSTEP_USER_ROOT is a GNUstep variable and Window Maker should not set it. Furthemore, it has been deprecated for 12 years already. As of gnustep-make/2.7.0-4 the GNUstep build system is configured in strict v2 mode which makes it impossible to compile GNUstep software. In a terminal started from a Window Maker session: yavor@aneto:/tmp/gorm.app-1.2.24$ make This is gnustep-make 2.7.0. Type 'make print-gnustep-make-help' for help. Running in gnustep-make version 2 strict mode. rm -f InterfaceBuilder; \ ln -s GormLib InterfaceBuilder /usr/share/GNUstep/Makefiles/config-noarch.make:121: *** GNUSTEP_USER_ROOT is obsolete. Stop. It is also impossible to build gnustep-make from pristine upstream source: yavor@aneto:/tmp$ wget -q ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.7.0.tar.gz yavor@aneto:/tmp$ tar xzf gnustep-make-2.7.0.tar.gz yavor@aneto:/tmp$ cd gnustep-make-2.7.0/ yavor@aneto:/tmp/gnustep-make-2.7.0$ ./configure ... yavor@aneto:/tmp/gnustep-make-2.7.0$ make config-noarch.make:121: *** GNUSTEP_USER_ROOT is obsolete. Stop. Note that the majority of GNUstep users use Window Maker as their window manager and many of them build GNUstep software from source, mostly because of the GNUstep Objective-C runtime which depends on Clang (Debian packages use GCC and the GCC/GNU runtime). Our solution is to replace the GNUSTEP_USER_ROOT environment variable with our own environment variable, WMAKER_USER_ROOT. This is documented in NEWS. [1] https://bugs.debian.org/922284
641 lines
14 KiB
C
641 lines
14 KiB
C
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "wconfig.h"
|
|
|
|
#include "WINGs.h"
|
|
#include "WINGsP.h"
|
|
#include "userdefaults.h"
|
|
|
|
|
|
typedef struct W_UserDefaults {
|
|
WMPropList *defaults;
|
|
|
|
WMPropList *appDomain;
|
|
|
|
WMPropList *searchListArray;
|
|
WMPropList **searchList; /* cache for searchListArray */
|
|
|
|
char dirty;
|
|
|
|
char dontSync;
|
|
|
|
char *path; /* where is db located */
|
|
|
|
time_t timestamp; /* last modification time */
|
|
|
|
struct W_UserDefaults *next;
|
|
|
|
} UserDefaults;
|
|
|
|
static UserDefaults *sharedUserDefaults = NULL;
|
|
|
|
char *WMUserDefaultsDidChangeNotification = "WMUserDefaultsDidChangeNotification";
|
|
|
|
static void synchronizeUserDefaults(void *foo);
|
|
|
|
#define DEFAULTS_DIR "/Defaults"
|
|
#ifndef HAVE_INOTIFY
|
|
/* Check defaults database for changes every this many milliseconds */
|
|
/* XXX: this is shared with src/ stuff, put it in some common header */
|
|
#define UD_SYNC_INTERVAL 2000
|
|
#endif
|
|
|
|
const char *wusergnusteppath()
|
|
{
|
|
static const char subdir[] = "/GNUstep";
|
|
static char *path = NULL;
|
|
char *gspath, *h;
|
|
int pathlen;
|
|
|
|
if (path)
|
|
/* Value have been already computed, re-use it */
|
|
return path;
|
|
|
|
#ifdef HAVE_SECURE_GETENV
|
|
gspath = secure_getenv("WMAKER_USER_ROOT");
|
|
#else
|
|
gspath = getenv("WMAKER_USER_ROOT");
|
|
#endif
|
|
if (gspath) {
|
|
gspath = wexpandpath(gspath);
|
|
if (gspath) {
|
|
path = gspath;
|
|
return path;
|
|
}
|
|
wwarning(_("variable WMAKER_USER_ROOT defined with invalid path, not used"));
|
|
}
|
|
|
|
h = wgethomedir();
|
|
if (!h)
|
|
return NULL;
|
|
|
|
pathlen = strlen(h);
|
|
path = wmalloc(pathlen + sizeof(subdir));
|
|
strcpy(path, h);
|
|
strcpy(path + pathlen, subdir);
|
|
|
|
return path;
|
|
}
|
|
|
|
char *wdefaultspathfordomain(const char *domain)
|
|
{
|
|
char *path;
|
|
const char *gspath;
|
|
size_t slen;
|
|
|
|
gspath = wusergnusteppath();
|
|
slen = strlen(gspath) + strlen(DEFAULTS_DIR) + strlen(domain) + 4;
|
|
path = wmalloc(slen);
|
|
|
|
strcpy(path, gspath);
|
|
strcat(path, DEFAULTS_DIR);
|
|
strcat(path, "/");
|
|
strcat(path, domain);
|
|
|
|
return path;
|
|
}
|
|
|
|
/* XXX: doesn't quite belong to *user*defaults.c */
|
|
char *wglobaldefaultspathfordomain(const char *domain)
|
|
{
|
|
char *t = NULL;
|
|
size_t len;
|
|
|
|
len = strlen(DEFSDATADIR) + strlen(domain) + 2;
|
|
t = wmalloc(len);
|
|
snprintf(t, len, "%s/%s", DEFSDATADIR, domain);
|
|
|
|
return t;
|
|
}
|
|
|
|
void w_save_defaults_changes(void)
|
|
{
|
|
if (WMApplication.applicationName == NULL) {
|
|
/*
|
|
* This means that the user has properly exited by calling the
|
|
* function 'WMReleaseApplication' (which has already called us)
|
|
* but we're being called again by the fallback 'atexit' method
|
|
* (the legacy way of saving changes on exit which is kept for
|
|
* application that would forget to call 'WMReleaseApplication')
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/* save the user defaults databases */
|
|
synchronizeUserDefaults(NULL);
|
|
}
|
|
|
|
/* set to save changes in defaults when program is exited */
|
|
static void registerSaveOnExit(void)
|
|
{
|
|
static Bool registeredSaveOnExit = False;
|
|
|
|
if (!registeredSaveOnExit) {
|
|
atexit(w_save_defaults_changes);
|
|
registeredSaveOnExit = True;
|
|
}
|
|
}
|
|
|
|
static void synchronizeUserDefaults(void *foo)
|
|
{
|
|
UserDefaults *database = sharedUserDefaults;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) foo;
|
|
|
|
while (database) {
|
|
if (!database->dontSync)
|
|
WMSynchronizeUserDefaults(database);
|
|
database = database->next;
|
|
}
|
|
}
|
|
|
|
#ifndef HAVE_INOTIFY
|
|
static void addSynchronizeTimerHandler(void)
|
|
{
|
|
static Bool initialized = False;
|
|
|
|
if (!initialized) {
|
|
WMAddPersistentTimerHandler(UD_SYNC_INTERVAL,
|
|
synchronizeUserDefaults, NULL);
|
|
initialized = True;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void WMEnableUDPeriodicSynchronization(WMUserDefaults * database, Bool enable)
|
|
{
|
|
database->dontSync = !enable;
|
|
}
|
|
|
|
void WMSynchronizeUserDefaults(WMUserDefaults * database)
|
|
{
|
|
Bool fileIsNewer = False, release = False, notify = False;
|
|
WMPropList *plF, *key;
|
|
char *path;
|
|
struct stat stbuf;
|
|
|
|
if (!database->path) {
|
|
path = wdefaultspathfordomain(WMGetApplicationName());
|
|
release = True;
|
|
} else {
|
|
path = database->path;
|
|
}
|
|
|
|
if (stat(path, &stbuf) >= 0 && stbuf.st_mtime > database->timestamp)
|
|
fileIsNewer = True;
|
|
|
|
if (database->appDomain && (database->dirty || fileIsNewer)) {
|
|
if (database->dirty && fileIsNewer) {
|
|
plF = WMReadPropListFromFile(path);
|
|
if (plF) {
|
|
plF = WMMergePLDictionaries(plF, database->appDomain, False);
|
|
WMReleasePropList(database->appDomain);
|
|
database->appDomain = plF;
|
|
key = database->searchList[0];
|
|
WMPutInPLDictionary(database->defaults, key, plF);
|
|
notify = True;
|
|
} else {
|
|
/* something happened with the file. just overwrite it */
|
|
wwarning(_("cannot read domain from file '%s' when syncing"), path);
|
|
WMWritePropListToFile(database->appDomain, path);
|
|
}
|
|
} else if (database->dirty) {
|
|
WMWritePropListToFile(database->appDomain, path);
|
|
} else if (fileIsNewer) {
|
|
plF = WMReadPropListFromFile(path);
|
|
if (plF) {
|
|
WMReleasePropList(database->appDomain);
|
|
database->appDomain = plF;
|
|
key = database->searchList[0];
|
|
WMPutInPLDictionary(database->defaults, key, plF);
|
|
notify = True;
|
|
} else {
|
|
/* something happened with the file. just overwrite it */
|
|
wwarning(_("cannot read domain from file '%s' when syncing"), path);
|
|
WMWritePropListToFile(database->appDomain, path);
|
|
}
|
|
}
|
|
|
|
database->dirty = 0;
|
|
|
|
if (stat(path, &stbuf) >= 0)
|
|
database->timestamp = stbuf.st_mtime;
|
|
|
|
if (notify) {
|
|
WMPostNotificationName(WMUserDefaultsDidChangeNotification, database, NULL);
|
|
}
|
|
}
|
|
|
|
if (release)
|
|
wfree(path);
|
|
|
|
}
|
|
|
|
void WMSaveUserDefaults(WMUserDefaults * database)
|
|
{
|
|
if (database->appDomain) {
|
|
struct stat stbuf;
|
|
char *path;
|
|
Bool release = False;
|
|
|
|
if (!database->path) {
|
|
path = wdefaultspathfordomain(WMGetApplicationName());
|
|
release = True;
|
|
} else {
|
|
path = database->path;
|
|
}
|
|
WMWritePropListToFile(database->appDomain, path);
|
|
database->dirty = 0;
|
|
if (stat(path, &stbuf) >= 0)
|
|
database->timestamp = stbuf.st_mtime;
|
|
if (release)
|
|
wfree(path);
|
|
}
|
|
}
|
|
|
|
WMUserDefaults *WMGetStandardUserDefaults(void)
|
|
{
|
|
WMUserDefaults *defaults;
|
|
WMPropList *domain;
|
|
WMPropList *key;
|
|
struct stat stbuf;
|
|
char *path;
|
|
int i;
|
|
|
|
if (sharedUserDefaults) {
|
|
defaults = sharedUserDefaults;
|
|
while (defaults) {
|
|
/* path == NULL only for StandardUserDefaults db */
|
|
if (defaults->path == NULL)
|
|
return defaults;
|
|
defaults = defaults->next;
|
|
}
|
|
}
|
|
|
|
/* we didn't found the database we are looking for. Go read it. XXX: wtf? */
|
|
defaults = wmalloc(sizeof(WMUserDefaults));
|
|
defaults->defaults = WMCreatePLDictionary(NULL, NULL);
|
|
defaults->searchList = wmalloc(sizeof(WMPropList *) * 3);
|
|
|
|
/* application domain */
|
|
key = WMCreatePLString(WMGetApplicationName());
|
|
defaults->searchList[0] = key;
|
|
|
|
/* temporary kluge. wmaker handles synchronization itself */
|
|
if (strcmp(WMGetApplicationName(), "WindowMaker") == 0) {
|
|
defaults->dontSync = 1;
|
|
}
|
|
|
|
path = wdefaultspathfordomain(WMGetFromPLString(key));
|
|
|
|
if (stat(path, &stbuf) >= 0)
|
|
defaults->timestamp = stbuf.st_mtime;
|
|
|
|
domain = WMReadPropListFromFile(path);
|
|
|
|
if (!domain)
|
|
domain = WMCreatePLDictionary(NULL, NULL);
|
|
|
|
wfree(path);
|
|
|
|
defaults->appDomain = domain;
|
|
|
|
if (domain)
|
|
WMPutInPLDictionary(defaults->defaults, key, domain);
|
|
|
|
/* global domain */
|
|
key = WMCreatePLString("WMGLOBAL");
|
|
defaults->searchList[1] = key;
|
|
|
|
path = wdefaultspathfordomain(WMGetFromPLString(key));
|
|
|
|
domain = WMReadPropListFromFile(path);
|
|
|
|
wfree(path);
|
|
|
|
if (!domain)
|
|
domain = WMCreatePLDictionary(NULL, NULL);
|
|
|
|
if (domain)
|
|
WMPutInPLDictionary(defaults->defaults, key, domain);
|
|
|
|
/* terminate list */
|
|
defaults->searchList[2] = NULL;
|
|
|
|
defaults->searchListArray = WMCreatePLArray(NULL, NULL);
|
|
|
|
i = 0;
|
|
while (defaults->searchList[i]) {
|
|
WMAddToPLArray(defaults->searchListArray, defaults->searchList[i]);
|
|
i++;
|
|
}
|
|
|
|
if (sharedUserDefaults)
|
|
defaults->next = sharedUserDefaults;
|
|
sharedUserDefaults = defaults;
|
|
|
|
#ifndef HAVE_INOTIFY
|
|
addSynchronizeTimerHandler();
|
|
#endif
|
|
registerSaveOnExit();
|
|
|
|
return defaults;
|
|
}
|
|
|
|
WMUserDefaults *WMGetDefaultsFromPath(const char *path)
|
|
{
|
|
WMUserDefaults *defaults;
|
|
WMPropList *domain;
|
|
WMPropList *key;
|
|
struct stat stbuf;
|
|
const char *name;
|
|
int i;
|
|
|
|
assert(path != NULL);
|
|
|
|
if (sharedUserDefaults) {
|
|
defaults = sharedUserDefaults;
|
|
while (defaults) {
|
|
if (defaults->path && strcmp(defaults->path, path) == 0)
|
|
return defaults;
|
|
defaults = defaults->next;
|
|
}
|
|
}
|
|
|
|
/* we didn't found the database we are looking for. Go read it. XXX wtf? */
|
|
defaults = wmalloc(sizeof(WMUserDefaults));
|
|
defaults->defaults = WMCreatePLDictionary(NULL, NULL);
|
|
defaults->searchList = wmalloc(sizeof(WMPropList *) * 2);
|
|
|
|
/* the domain we want, go in the first position */
|
|
name = strrchr(path, '/');
|
|
if (!name)
|
|
name = path;
|
|
else
|
|
name++;
|
|
|
|
key = WMCreatePLString(name);
|
|
defaults->searchList[0] = key;
|
|
|
|
if (stat(path, &stbuf) >= 0)
|
|
defaults->timestamp = stbuf.st_mtime;
|
|
|
|
domain = WMReadPropListFromFile(path);
|
|
|
|
if (!domain)
|
|
domain = WMCreatePLDictionary(NULL, NULL);
|
|
|
|
defaults->path = wstrdup(path);
|
|
|
|
defaults->appDomain = domain;
|
|
|
|
if (domain)
|
|
WMPutInPLDictionary(defaults->defaults, key, domain);
|
|
|
|
/* terminate list */
|
|
defaults->searchList[1] = NULL;
|
|
|
|
defaults->searchListArray = WMCreatePLArray(NULL, NULL);
|
|
|
|
i = 0;
|
|
while (defaults->searchList[i]) {
|
|
WMAddToPLArray(defaults->searchListArray, defaults->searchList[i]);
|
|
i++;
|
|
}
|
|
|
|
if (sharedUserDefaults)
|
|
defaults->next = sharedUserDefaults;
|
|
sharedUserDefaults = defaults;
|
|
|
|
#ifndef HAVE_INOTIFY
|
|
addSynchronizeTimerHandler();
|
|
#endif
|
|
registerSaveOnExit();
|
|
|
|
return defaults;
|
|
}
|
|
|
|
/* Returns a WMPropList array with all keys in the user defaults database.
|
|
* Free the array with WMReleasePropList() when no longer needed,
|
|
* but do not free the elements of the array! They're just references. */
|
|
WMPropList *WMGetUDKeys(WMUserDefaults * database)
|
|
{
|
|
return WMGetPLDictionaryKeys(database->appDomain);
|
|
}
|
|
|
|
WMPropList *WMGetUDObjectForKey(WMUserDefaults * database, const char *defaultName)
|
|
{
|
|
WMPropList *domainName, *domain;
|
|
WMPropList *object = NULL;
|
|
WMPropList *key = WMCreatePLString(defaultName);
|
|
int i = 0;
|
|
|
|
while (database->searchList[i] && !object) {
|
|
domainName = database->searchList[i];
|
|
domain = WMGetFromPLDictionary(database->defaults, domainName);
|
|
if (domain) {
|
|
object = WMGetFromPLDictionary(domain, key);
|
|
}
|
|
i++;
|
|
}
|
|
WMReleasePropList(key);
|
|
|
|
return object;
|
|
}
|
|
|
|
void WMSetUDObjectForKey(WMUserDefaults * database, WMPropList * object, const char *defaultName)
|
|
{
|
|
WMPropList *key = WMCreatePLString(defaultName);
|
|
|
|
database->dirty = 1;
|
|
|
|
WMPutInPLDictionary(database->appDomain, key, object);
|
|
WMReleasePropList(key);
|
|
}
|
|
|
|
void WMRemoveUDObjectForKey(WMUserDefaults * database, const char *defaultName)
|
|
{
|
|
WMPropList *key = WMCreatePLString(defaultName);
|
|
|
|
database->dirty = 1;
|
|
|
|
WMRemoveFromPLDictionary(database->appDomain, key);
|
|
|
|
WMReleasePropList(key);
|
|
}
|
|
|
|
char *WMGetUDStringForKey(WMUserDefaults * database, const char *defaultName)
|
|
{
|
|
WMPropList *val;
|
|
|
|
val = WMGetUDObjectForKey(database, defaultName);
|
|
|
|
if (!val)
|
|
return NULL;
|
|
|
|
if (!WMIsPLString(val))
|
|
return NULL;
|
|
|
|
return WMGetFromPLString(val);
|
|
}
|
|
|
|
int WMGetUDIntegerForKey(WMUserDefaults * database, const char *defaultName)
|
|
{
|
|
WMPropList *val;
|
|
char *str;
|
|
int value;
|
|
|
|
val = WMGetUDObjectForKey(database, defaultName);
|
|
|
|
if (!val)
|
|
return 0;
|
|
|
|
if (!WMIsPLString(val))
|
|
return 0;
|
|
|
|
str = WMGetFromPLString(val);
|
|
if (!str)
|
|
return 0;
|
|
|
|
if (sscanf(str, "%i", &value) != 1)
|
|
return 0;
|
|
|
|
return value;
|
|
}
|
|
|
|
float WMGetUDFloatForKey(WMUserDefaults * database, const char *defaultName)
|
|
{
|
|
WMPropList *val;
|
|
char *str;
|
|
float value;
|
|
|
|
val = WMGetUDObjectForKey(database, defaultName);
|
|
|
|
if (!val || !WMIsPLString(val))
|
|
return 0.0;
|
|
|
|
if (!(str = WMGetFromPLString(val)))
|
|
return 0.0;
|
|
|
|
if (sscanf(str, "%f", &value) != 1)
|
|
return 0.0;
|
|
|
|
return value;
|
|
}
|
|
|
|
Bool WMGetUDBoolForKey(WMUserDefaults * database, const char *defaultName)
|
|
{
|
|
WMPropList *val;
|
|
int value;
|
|
char *str;
|
|
|
|
val = WMGetUDObjectForKey(database, defaultName);
|
|
|
|
if (!val)
|
|
return False;
|
|
|
|
if (!WMIsPLString(val))
|
|
return False;
|
|
|
|
str = WMGetFromPLString(val);
|
|
if (!str)
|
|
return False;
|
|
|
|
if (sscanf(str, "%i", &value) == 1 && value != 0)
|
|
return True;
|
|
|
|
if (strcasecmp(str, "YES") == 0)
|
|
return True;
|
|
|
|
if (strcasecmp(str, "Y") == 0)
|
|
return True;
|
|
|
|
return False;
|
|
}
|
|
|
|
void WMSetUDIntegerForKey(WMUserDefaults * database, int value, const char *defaultName)
|
|
{
|
|
WMPropList *object;
|
|
char buffer[128];
|
|
|
|
sprintf(buffer, "%i", value);
|
|
object = WMCreatePLString(buffer);
|
|
|
|
WMSetUDObjectForKey(database, object, defaultName);
|
|
WMReleasePropList(object);
|
|
}
|
|
|
|
void WMSetUDStringForKey(WMUserDefaults * database, const char *value, const char *defaultName)
|
|
{
|
|
WMPropList *object;
|
|
|
|
object = WMCreatePLString(value);
|
|
|
|
WMSetUDObjectForKey(database, object, defaultName);
|
|
WMReleasePropList(object);
|
|
}
|
|
|
|
void WMSetUDFloatForKey(WMUserDefaults * database, float value, const char *defaultName)
|
|
{
|
|
WMPropList *object;
|
|
char buffer[128];
|
|
|
|
sprintf(buffer, "%f", (double)value);
|
|
object = WMCreatePLString(buffer);
|
|
|
|
WMSetUDObjectForKey(database, object, defaultName);
|
|
WMReleasePropList(object);
|
|
}
|
|
|
|
void WMSetUDBoolForKey(WMUserDefaults * database, Bool value, const char *defaultName)
|
|
{
|
|
static WMPropList *yes = NULL, *no = NULL;
|
|
|
|
if (!yes) {
|
|
yes = WMCreatePLString("YES");
|
|
no = WMCreatePLString("NO");
|
|
}
|
|
|
|
WMSetUDObjectForKey(database, value ? yes : no, defaultName);
|
|
}
|
|
|
|
WMPropList *WMGetUDSearchList(WMUserDefaults * database)
|
|
{
|
|
return database->searchListArray;
|
|
}
|
|
|
|
void WMSetUDSearchList(WMUserDefaults * database, WMPropList * list)
|
|
{
|
|
int i, c;
|
|
|
|
if (database->searchList) {
|
|
i = 0;
|
|
while (database->searchList[i]) {
|
|
WMReleasePropList(database->searchList[i]);
|
|
i++;
|
|
}
|
|
wfree(database->searchList);
|
|
}
|
|
if (database->searchListArray) {
|
|
WMReleasePropList(database->searchListArray);
|
|
}
|
|
|
|
c = WMGetPropListItemCount(list);
|
|
database->searchList = wmalloc(sizeof(WMPropList *) * (c + 1));
|
|
|
|
for (i = 0; i < c; i++) {
|
|
database->searchList[i] = WMGetFromPLArray(list, i);
|
|
}
|
|
database->searchList[c] = NULL;
|
|
|
|
database->searchListArray = WMDeepCopyPropList(list);
|
|
}
|