14 #include "userdefaults.h"
17 typedef struct W_UserDefaults
{
20 WMPropList
*appDomain
;
22 WMPropList
*searchListArray
;
23 WMPropList
**searchList
; /* cache for searchListArray */
29 char *path
; /* where is db located */
31 time_t timestamp
; /* last modification time */
33 struct W_UserDefaults
*next
;
37 static UserDefaults
*sharedUserDefaults
= NULL
;
39 char *WMUserDefaultsDidChangeNotification
= "WMUserDefaultsDidChangeNotification";
41 static void synchronizeUserDefaults(void *foo
);
43 #define DEFAULTS_DIR "/Defaults"
45 /* Check defaults database for changes every this many milliseconds */
46 /* XXX: this is shared with src/ stuff, put it in some common header */
47 #define UD_SYNC_INTERVAL 2000
50 const char *wusergnusteppath()
52 static const char subdir
[] = "/GNUstep";
53 static char *path
= NULL
;
58 /* Value have been already computed, re-use it */
61 #ifdef HAVE_SECURE_GETENV
62 gspath
= secure_getenv("GNUSTEP_USER_ROOT");
64 gspath
= getenv("GNUSTEP_USER_ROOT");
67 gspath
= wexpandpath(gspath
);
72 wwarning(_("variable GNUSTEP_USER_ROOT defined with invalid path, not used"));
80 path
= wmalloc(pathlen
+ sizeof(subdir
));
82 strcpy(path
+ pathlen
, subdir
);
87 char *wdefaultspathfordomain(const char *domain
)
93 gspath
= wusergnusteppath();
94 slen
= strlen(gspath
) + strlen(DEFAULTS_DIR
) + strlen(domain
) + 4;
98 strcat(path
, DEFAULTS_DIR
);
100 strcat(path
, domain
);
105 /* XXX: doesn't quite belong to *user*defaults.c */
106 char *wglobaldefaultspathfordomain(const char *domain
)
111 len
= strlen(DEFSDATADIR
) + strlen(domain
) + 2;
113 snprintf(t
, len
, "%s/%s", DEFSDATADIR
, domain
);
118 void w_save_defaults_changes(void)
120 if (WMApplication
.applicationName
== NULL
) {
122 * This means that the user has properly exited by calling the
123 * function 'WMReleaseApplication' (which has already called us)
124 * but we're being called again by the fallback 'atexit' method
125 * (the legacy way of saving changes on exit which is kept for
126 * application that would forget to call 'WMReleaseApplication')
131 /* save the user defaults databases */
132 synchronizeUserDefaults(NULL
);
135 /* set to save changes in defaults when program is exited */
136 static void registerSaveOnExit(void)
138 static Bool registeredSaveOnExit
= False
;
140 if (!registeredSaveOnExit
) {
141 atexit(w_save_defaults_changes
);
142 registeredSaveOnExit
= True
;
146 static void synchronizeUserDefaults(void *foo
)
148 UserDefaults
*database
= sharedUserDefaults
;
150 /* Parameter not used, but tell the compiler that it is ok */
154 if (!database
->dontSync
)
155 WMSynchronizeUserDefaults(database
);
156 database
= database
->next
;
161 static void addSynchronizeTimerHandler(void)
163 static Bool initialized
= False
;
166 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL
,
167 synchronizeUserDefaults
, NULL
);
173 void WMEnableUDPeriodicSynchronization(WMUserDefaults
* database
, Bool enable
)
175 database
->dontSync
= !enable
;
178 void WMSynchronizeUserDefaults(WMUserDefaults
* database
)
180 Bool fileIsNewer
= False
, release
= False
, notify
= False
;
181 WMPropList
*plF
, *key
;
185 if (!database
->path
) {
186 path
= wdefaultspathfordomain(WMGetApplicationName());
189 path
= database
->path
;
192 if (stat(path
, &stbuf
) >= 0 && stbuf
.st_mtime
> database
->timestamp
)
195 if (database
->appDomain
&& (database
->dirty
|| fileIsNewer
)) {
196 if (database
->dirty
&& fileIsNewer
) {
197 plF
= WMReadPropListFromFile(path
);
199 plF
= WMMergePLDictionaries(plF
, database
->appDomain
, False
);
200 WMReleasePropList(database
->appDomain
);
201 database
->appDomain
= plF
;
202 key
= database
->searchList
[0];
203 WMPutInPLDictionary(database
->defaults
, key
, plF
);
206 /* something happened with the file. just overwrite it */
207 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
208 WMWritePropListToFile(database
->appDomain
, path
);
210 } else if (database
->dirty
) {
211 WMWritePropListToFile(database
->appDomain
, path
);
212 } else if (fileIsNewer
) {
213 plF
= WMReadPropListFromFile(path
);
215 WMReleasePropList(database
->appDomain
);
216 database
->appDomain
= plF
;
217 key
= database
->searchList
[0];
218 WMPutInPLDictionary(database
->defaults
, key
, plF
);
221 /* something happened with the file. just overwrite it */
222 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
223 WMWritePropListToFile(database
->appDomain
, path
);
229 if (stat(path
, &stbuf
) >= 0)
230 database
->timestamp
= stbuf
.st_mtime
;
233 WMPostNotificationName(WMUserDefaultsDidChangeNotification
, database
, NULL
);
242 void WMSaveUserDefaults(WMUserDefaults
* database
)
244 if (database
->appDomain
) {
247 Bool release
= False
;
249 if (!database
->path
) {
250 path
= wdefaultspathfordomain(WMGetApplicationName());
253 path
= database
->path
;
255 WMWritePropListToFile(database
->appDomain
, path
);
257 if (stat(path
, &stbuf
) >= 0)
258 database
->timestamp
= stbuf
.st_mtime
;
264 WMUserDefaults
*WMGetStandardUserDefaults(void)
266 WMUserDefaults
*defaults
;
273 if (sharedUserDefaults
) {
274 defaults
= sharedUserDefaults
;
276 /* path == NULL only for StandardUserDefaults db */
277 if (defaults
->path
== NULL
)
279 defaults
= defaults
->next
;
283 /* we didn't found the database we are looking for. Go read it. XXX: wtf? */
284 defaults
= wmalloc(sizeof(WMUserDefaults
));
285 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
286 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 3);
288 /* application domain */
289 key
= WMCreatePLString(WMGetApplicationName());
290 defaults
->searchList
[0] = key
;
292 /* temporary kluge. wmaker handles synchronization itself */
293 if (strcmp(WMGetApplicationName(), "WindowMaker") == 0) {
294 defaults
->dontSync
= 1;
297 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
299 if (stat(path
, &stbuf
) >= 0)
300 defaults
->timestamp
= stbuf
.st_mtime
;
302 domain
= WMReadPropListFromFile(path
);
305 domain
= WMCreatePLDictionary(NULL
, NULL
);
309 defaults
->appDomain
= domain
;
312 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
315 key
= WMCreatePLString("WMGLOBAL");
316 defaults
->searchList
[1] = key
;
318 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
320 domain
= WMReadPropListFromFile(path
);
325 domain
= WMCreatePLDictionary(NULL
, NULL
);
328 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
331 defaults
->searchList
[2] = NULL
;
333 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
336 while (defaults
->searchList
[i
]) {
337 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
341 if (sharedUserDefaults
)
342 defaults
->next
= sharedUserDefaults
;
343 sharedUserDefaults
= defaults
;
346 addSynchronizeTimerHandler();
348 registerSaveOnExit();
353 WMUserDefaults
*WMGetDefaultsFromPath(const char *path
)
355 WMUserDefaults
*defaults
;
362 assert(path
!= NULL
);
364 if (sharedUserDefaults
) {
365 defaults
= sharedUserDefaults
;
367 if (defaults
->path
&& strcmp(defaults
->path
, path
) == 0)
369 defaults
= defaults
->next
;
373 /* we didn't found the database we are looking for. Go read it. XXX wtf? */
374 defaults
= wmalloc(sizeof(WMUserDefaults
));
375 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
376 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 2);
378 /* the domain we want, go in the first position */
379 name
= strrchr(path
, '/');
385 key
= WMCreatePLString(name
);
386 defaults
->searchList
[0] = key
;
388 if (stat(path
, &stbuf
) >= 0)
389 defaults
->timestamp
= stbuf
.st_mtime
;
391 domain
= WMReadPropListFromFile(path
);
394 domain
= WMCreatePLDictionary(NULL
, NULL
);
396 defaults
->path
= wstrdup(path
);
398 defaults
->appDomain
= domain
;
401 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
404 defaults
->searchList
[1] = NULL
;
406 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
409 while (defaults
->searchList
[i
]) {
410 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
414 if (sharedUserDefaults
)
415 defaults
->next
= sharedUserDefaults
;
416 sharedUserDefaults
= defaults
;
419 addSynchronizeTimerHandler();
421 registerSaveOnExit();
426 /* Returns a WMPropList array with all keys in the user defaults database.
427 * Free the array with WMReleasePropList() when no longer needed,
428 * but do not free the elements of the array! They're just references. */
429 WMPropList
*WMGetUDKeys(WMUserDefaults
* database
)
431 return WMGetPLDictionaryKeys(database
->appDomain
);
434 WMPropList
*WMGetUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
436 WMPropList
*domainName
, *domain
;
437 WMPropList
*object
= NULL
;
438 WMPropList
*key
= WMCreatePLString(defaultName
);
441 while (database
->searchList
[i
] && !object
) {
442 domainName
= database
->searchList
[i
];
443 domain
= WMGetFromPLDictionary(database
->defaults
, domainName
);
445 object
= WMGetFromPLDictionary(domain
, key
);
449 WMReleasePropList(key
);
454 void WMSetUDObjectForKey(WMUserDefaults
* database
, WMPropList
* object
, const char *defaultName
)
456 WMPropList
*key
= WMCreatePLString(defaultName
);
460 WMPutInPLDictionary(database
->appDomain
, key
, object
);
461 WMReleasePropList(key
);
464 void WMRemoveUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
466 WMPropList
*key
= WMCreatePLString(defaultName
);
470 WMRemoveFromPLDictionary(database
->appDomain
, key
);
472 WMReleasePropList(key
);
475 char *WMGetUDStringForKey(WMUserDefaults
* database
, const char *defaultName
)
479 val
= WMGetUDObjectForKey(database
, defaultName
);
484 if (!WMIsPLString(val
))
487 return WMGetFromPLString(val
);
490 int WMGetUDIntegerForKey(WMUserDefaults
* database
, const char *defaultName
)
496 val
= WMGetUDObjectForKey(database
, defaultName
);
501 if (!WMIsPLString(val
))
504 str
= WMGetFromPLString(val
);
508 if (sscanf(str
, "%i", &value
) != 1)
514 float WMGetUDFloatForKey(WMUserDefaults
* database
, const char *defaultName
)
520 val
= WMGetUDObjectForKey(database
, defaultName
);
522 if (!val
|| !WMIsPLString(val
))
525 if (!(str
= WMGetFromPLString(val
)))
528 if (sscanf(str
, "%f", &value
) != 1)
534 Bool
WMGetUDBoolForKey(WMUserDefaults
* database
, const char *defaultName
)
540 val
= WMGetUDObjectForKey(database
, defaultName
);
545 if (!WMIsPLString(val
))
548 str
= WMGetFromPLString(val
);
552 if (sscanf(str
, "%i", &value
) == 1 && value
!= 0)
555 if (strcasecmp(str
, "YES") == 0)
558 if (strcasecmp(str
, "Y") == 0)
564 void WMSetUDIntegerForKey(WMUserDefaults
* database
, int value
, const char *defaultName
)
569 sprintf(buffer
, "%i", value
);
570 object
= WMCreatePLString(buffer
);
572 WMSetUDObjectForKey(database
, object
, defaultName
);
573 WMReleasePropList(object
);
576 void WMSetUDStringForKey(WMUserDefaults
* database
, const char *value
, const char *defaultName
)
580 object
= WMCreatePLString(value
);
582 WMSetUDObjectForKey(database
, object
, defaultName
);
583 WMReleasePropList(object
);
586 void WMSetUDFloatForKey(WMUserDefaults
* database
, float value
, const char *defaultName
)
591 sprintf(buffer
, "%f", (double)value
);
592 object
= WMCreatePLString(buffer
);
594 WMSetUDObjectForKey(database
, object
, defaultName
);
595 WMReleasePropList(object
);
598 void WMSetUDBoolForKey(WMUserDefaults
* database
, Bool value
, const char *defaultName
)
600 static WMPropList
*yes
= NULL
, *no
= NULL
;
603 yes
= WMCreatePLString("YES");
604 no
= WMCreatePLString("NO");
607 WMSetUDObjectForKey(database
, value
? yes
: no
, defaultName
);
610 WMPropList
*WMGetUDSearchList(WMUserDefaults
* database
)
612 return database
->searchListArray
;
615 void WMSetUDSearchList(WMUserDefaults
* database
, WMPropList
* list
)
619 if (database
->searchList
) {
621 while (database
->searchList
[i
]) {
622 WMReleasePropList(database
->searchList
[i
]);
625 wfree(database
->searchList
);
627 if (database
->searchListArray
) {
628 WMReleasePropList(database
->searchListArray
);
631 c
= WMGetPropListItemCount(list
);
632 database
->searchList
= wmalloc(sizeof(WMPropList
*) * (c
+ 1));
634 for (i
= 0; i
< c
; i
++) {
635 database
->searchList
[i
] = WMGetFromPLArray(list
, i
);
637 database
->searchList
[c
] = NULL
;
639 database
->searchListArray
= WMDeepCopyPropList(list
);