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 #ifndef GLOBAL_DEFAULTS_SUBDIR
107 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
109 char *wglobaldefaultspathfordomain(const char *domain
)
114 len
= strlen( SYSCONFDIR
) + strlen( GLOBAL_DEFAULTS_SUBDIR
) + strlen(domain
) + 3;
116 snprintf(t
, len
, "%s/%s/%s", SYSCONFDIR
, GLOBAL_DEFAULTS_SUBDIR
, domain
);
121 void w_save_defaults_changes(void)
123 if (WMApplication
.applicationName
== NULL
) {
125 * This means that the user has properly exited by calling the
126 * function 'WMReleaseApplication' (which has already called us)
127 * but we're being called again by the fallback 'atexit' method
128 * (the legacy way of saving changes on exit which is kept for
129 * application that would forget to call 'WMReleaseApplication')
134 /* save the user defaults databases */
135 synchronizeUserDefaults(NULL
);
138 /* set to save changes in defaults when program is exited */
139 static void registerSaveOnExit(void)
141 static Bool registeredSaveOnExit
= False
;
143 if (!registeredSaveOnExit
) {
144 atexit(w_save_defaults_changes
);
145 registeredSaveOnExit
= True
;
149 static void synchronizeUserDefaults(void *foo
)
151 UserDefaults
*database
= sharedUserDefaults
;
153 /* Parameter not used, but tell the compiler that it is ok */
157 if (!database
->dontSync
)
158 WMSynchronizeUserDefaults(database
);
159 database
= database
->next
;
164 static void addSynchronizeTimerHandler(void)
166 static Bool initialized
= False
;
169 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL
,
170 synchronizeUserDefaults
, NULL
);
176 void WMEnableUDPeriodicSynchronization(WMUserDefaults
* database
, Bool enable
)
178 database
->dontSync
= !enable
;
181 void WMSynchronizeUserDefaults(WMUserDefaults
* database
)
183 Bool fileIsNewer
= False
, release
= False
, notify
= False
;
184 WMPropList
*plF
, *key
;
188 if (!database
->path
) {
189 path
= wdefaultspathfordomain(WMGetApplicationName());
192 path
= database
->path
;
195 if (stat(path
, &stbuf
) >= 0 && stbuf
.st_mtime
> database
->timestamp
)
198 if (database
->appDomain
&& (database
->dirty
|| fileIsNewer
)) {
199 if (database
->dirty
&& fileIsNewer
) {
200 plF
= WMReadPropListFromFile(path
);
202 plF
= WMMergePLDictionaries(plF
, database
->appDomain
, False
);
203 WMReleasePropList(database
->appDomain
);
204 database
->appDomain
= plF
;
205 key
= database
->searchList
[0];
206 WMPutInPLDictionary(database
->defaults
, key
, plF
);
209 /* something happened with the file. just overwrite it */
210 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
211 WMWritePropListToFile(database
->appDomain
, path
);
213 } else if (database
->dirty
) {
214 WMWritePropListToFile(database
->appDomain
, path
);
215 } else if (fileIsNewer
) {
216 plF
= WMReadPropListFromFile(path
);
218 WMReleasePropList(database
->appDomain
);
219 database
->appDomain
= plF
;
220 key
= database
->searchList
[0];
221 WMPutInPLDictionary(database
->defaults
, key
, plF
);
224 /* something happened with the file. just overwrite it */
225 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
226 WMWritePropListToFile(database
->appDomain
, path
);
232 if (stat(path
, &stbuf
) >= 0)
233 database
->timestamp
= stbuf
.st_mtime
;
236 WMPostNotificationName(WMUserDefaultsDidChangeNotification
, database
, NULL
);
245 void WMSaveUserDefaults(WMUserDefaults
* database
)
247 if (database
->appDomain
) {
250 Bool release
= False
;
252 if (!database
->path
) {
253 path
= wdefaultspathfordomain(WMGetApplicationName());
256 path
= database
->path
;
258 WMWritePropListToFile(database
->appDomain
, path
);
260 if (stat(path
, &stbuf
) >= 0)
261 database
->timestamp
= stbuf
.st_mtime
;
267 WMUserDefaults
*WMGetStandardUserDefaults(void)
269 WMUserDefaults
*defaults
;
276 if (sharedUserDefaults
) {
277 defaults
= sharedUserDefaults
;
279 /* path == NULL only for StandardUserDefaults db */
280 if (defaults
->path
== NULL
)
282 defaults
= defaults
->next
;
286 /* we didn't found the database we are looking for. Go read it. XXX: wtf? */
287 defaults
= wmalloc(sizeof(WMUserDefaults
));
288 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
289 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 3);
291 /* application domain */
292 key
= WMCreatePLString(WMGetApplicationName());
293 defaults
->searchList
[0] = key
;
295 /* temporary kluge. wmaker handles synchronization itself */
296 if (strcmp(WMGetApplicationName(), "WindowMaker") == 0) {
297 defaults
->dontSync
= 1;
300 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
302 if (stat(path
, &stbuf
) >= 0)
303 defaults
->timestamp
= stbuf
.st_mtime
;
305 domain
= WMReadPropListFromFile(path
);
308 domain
= WMCreatePLDictionary(NULL
, NULL
);
312 defaults
->appDomain
= domain
;
315 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
318 key
= WMCreatePLString("WMGLOBAL");
319 defaults
->searchList
[1] = key
;
321 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
323 domain
= WMReadPropListFromFile(path
);
328 domain
= WMCreatePLDictionary(NULL
, NULL
);
331 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
334 defaults
->searchList
[2] = NULL
;
336 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
339 while (defaults
->searchList
[i
]) {
340 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
344 if (sharedUserDefaults
)
345 defaults
->next
= sharedUserDefaults
;
346 sharedUserDefaults
= defaults
;
349 addSynchronizeTimerHandler();
351 registerSaveOnExit();
356 WMUserDefaults
*WMGetDefaultsFromPath(const char *path
)
358 WMUserDefaults
*defaults
;
365 assert(path
!= NULL
);
367 if (sharedUserDefaults
) {
368 defaults
= sharedUserDefaults
;
370 if (defaults
->path
&& strcmp(defaults
->path
, path
) == 0)
372 defaults
= defaults
->next
;
376 /* we didn't found the database we are looking for. Go read it. XXX wtf? */
377 defaults
= wmalloc(sizeof(WMUserDefaults
));
378 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
379 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 2);
381 /* the domain we want, go in the first position */
382 name
= strrchr(path
, '/');
388 key
= WMCreatePLString(name
);
389 defaults
->searchList
[0] = key
;
391 if (stat(path
, &stbuf
) >= 0)
392 defaults
->timestamp
= stbuf
.st_mtime
;
394 domain
= WMReadPropListFromFile(path
);
397 domain
= WMCreatePLDictionary(NULL
, NULL
);
399 defaults
->path
= wstrdup(path
);
401 defaults
->appDomain
= domain
;
404 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
407 defaults
->searchList
[1] = NULL
;
409 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
412 while (defaults
->searchList
[i
]) {
413 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
417 if (sharedUserDefaults
)
418 defaults
->next
= sharedUserDefaults
;
419 sharedUserDefaults
= defaults
;
422 addSynchronizeTimerHandler();
424 registerSaveOnExit();
429 /* Returns a WMPropList array with all keys in the user defaults database.
430 * Free the array with WMReleasePropList() when no longer needed,
431 * but do not free the elements of the array! They're just references. */
432 WMPropList
*WMGetUDKeys(WMUserDefaults
* database
)
434 return WMGetPLDictionaryKeys(database
->appDomain
);
437 WMPropList
*WMGetUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
439 WMPropList
*domainName
, *domain
;
440 WMPropList
*object
= NULL
;
441 WMPropList
*key
= WMCreatePLString(defaultName
);
444 while (database
->searchList
[i
] && !object
) {
445 domainName
= database
->searchList
[i
];
446 domain
= WMGetFromPLDictionary(database
->defaults
, domainName
);
448 object
= WMGetFromPLDictionary(domain
, key
);
452 WMReleasePropList(key
);
457 void WMSetUDObjectForKey(WMUserDefaults
* database
, WMPropList
* object
, const char *defaultName
)
459 WMPropList
*key
= WMCreatePLString(defaultName
);
463 WMPutInPLDictionary(database
->appDomain
, key
, object
);
464 WMReleasePropList(key
);
467 void WMRemoveUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
469 WMPropList
*key
= WMCreatePLString(defaultName
);
473 WMRemoveFromPLDictionary(database
->appDomain
, key
);
475 WMReleasePropList(key
);
478 char *WMGetUDStringForKey(WMUserDefaults
* database
, const char *defaultName
)
482 val
= WMGetUDObjectForKey(database
, defaultName
);
487 if (!WMIsPLString(val
))
490 return WMGetFromPLString(val
);
493 int WMGetUDIntegerForKey(WMUserDefaults
* database
, const char *defaultName
)
499 val
= WMGetUDObjectForKey(database
, defaultName
);
504 if (!WMIsPLString(val
))
507 str
= WMGetFromPLString(val
);
511 if (sscanf(str
, "%i", &value
) != 1)
517 float WMGetUDFloatForKey(WMUserDefaults
* database
, const char *defaultName
)
523 val
= WMGetUDObjectForKey(database
, defaultName
);
525 if (!val
|| !WMIsPLString(val
))
528 if (!(str
= WMGetFromPLString(val
)))
531 if (sscanf(str
, "%f", &value
) != 1)
537 Bool
WMGetUDBoolForKey(WMUserDefaults
* database
, const char *defaultName
)
543 val
= WMGetUDObjectForKey(database
, defaultName
);
548 if (!WMIsPLString(val
))
551 str
= WMGetFromPLString(val
);
555 if (sscanf(str
, "%i", &value
) == 1 && value
!= 0)
558 if (strcasecmp(str
, "YES") == 0)
561 if (strcasecmp(str
, "Y") == 0)
567 void WMSetUDIntegerForKey(WMUserDefaults
* database
, int value
, const char *defaultName
)
572 sprintf(buffer
, "%i", value
);
573 object
= WMCreatePLString(buffer
);
575 WMSetUDObjectForKey(database
, object
, defaultName
);
576 WMReleasePropList(object
);
579 void WMSetUDStringForKey(WMUserDefaults
* database
, const char *value
, const char *defaultName
)
583 object
= WMCreatePLString(value
);
585 WMSetUDObjectForKey(database
, object
, defaultName
);
586 WMReleasePropList(object
);
589 void WMSetUDFloatForKey(WMUserDefaults
* database
, float value
, const char *defaultName
)
594 sprintf(buffer
, "%f", value
);
595 object
= WMCreatePLString(buffer
);
597 WMSetUDObjectForKey(database
, object
, defaultName
);
598 WMReleasePropList(object
);
601 void WMSetUDBoolForKey(WMUserDefaults
* database
, Bool value
, const char *defaultName
)
603 static WMPropList
*yes
= NULL
, *no
= NULL
;
606 yes
= WMCreatePLString("YES");
607 no
= WMCreatePLString("NO");
610 WMSetUDObjectForKey(database
, value
? yes
: no
, defaultName
);
613 WMPropList
*WMGetUDSearchList(WMUserDefaults
* database
)
615 return database
->searchListArray
;
618 void WMSetUDSearchList(WMUserDefaults
* database
, WMPropList
* list
)
622 if (database
->searchList
) {
624 while (database
->searchList
[i
]) {
625 WMReleasePropList(database
->searchList
[i
]);
628 wfree(database
->searchList
);
630 if (database
->searchListArray
) {
631 WMReleasePropList(database
->searchListArray
);
634 c
= WMGetPropListItemCount(list
);
635 database
->searchList
= wmalloc(sizeof(WMPropList
*) * (c
+ 1));
637 for (i
= 0; i
< c
; i
++) {
638 database
->searchList
[i
] = WMGetFromPLArray(list
, i
);
640 database
->searchList
[c
] = NULL
;
642 database
->searchListArray
= WMDeepCopyPropList(list
);