configure: Add option to specify global defaults directory.
[wmaker-crm.git] / WINGs / userdefaults.c
blob3ec776c61772fb086c01f63c379bedfebee147ab
2 #include <stdlib.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <stdio.h>
6 #include <assert.h>
7 #include <unistd.h>
8 #include <sys/stat.h>
10 #include "wconfig.h"
12 #include "WINGs.h"
13 #include "WINGsP.h"
14 #include "userdefaults.h"
17 typedef struct W_UserDefaults {
18 WMPropList *defaults;
20 WMPropList *appDomain;
22 WMPropList *searchListArray;
23 WMPropList **searchList; /* cache for searchListArray */
25 char dirty;
27 char dontSync;
29 char *path; /* where is db located */
31 time_t timestamp; /* last modification time */
33 struct W_UserDefaults *next;
35 } UserDefaults;
37 static UserDefaults *sharedUserDefaults = NULL;
39 char *WMUserDefaultsDidChangeNotification = "WMUserDefaultsDidChangeNotification";
41 static void synchronizeUserDefaults(void *foo);
43 #define DEFAULTS_DIR "/Defaults"
44 #ifndef HAVE_INOTIFY
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
48 #endif
50 const char *wusergnusteppath()
52 static const char subdir[] = "/GNUstep";
53 static char *path = NULL;
54 char *gspath, *h;
55 int pathlen;
57 if (path)
58 /* Value have been already computed, re-use it */
59 return path;
61 #ifdef HAVE_SECURE_GETENV
62 gspath = secure_getenv("GNUSTEP_USER_ROOT");
63 #else
64 gspath = getenv("GNUSTEP_USER_ROOT");
65 #endif
66 if (gspath) {
67 gspath = wexpandpath(gspath);
68 if (gspath) {
69 path = gspath;
70 return path;
72 wwarning(_("variable GNUSTEP_USER_ROOT defined with invalid path, not used"));
75 h = wgethomedir();
76 if (!h)
77 return NULL;
79 pathlen = strlen(h);
80 path = wmalloc(pathlen + sizeof(subdir));
81 strcpy(path, h);
82 strcpy(path + pathlen, subdir);
84 return path;
87 char *wdefaultspathfordomain(const char *domain)
89 char *path;
90 const char *gspath;
91 size_t slen;
93 gspath = wusergnusteppath();
94 slen = strlen(gspath) + strlen(DEFAULTS_DIR) + strlen(domain) + 4;
95 path = wmalloc(slen);
97 strcpy(path, gspath);
98 strcat(path, DEFAULTS_DIR);
99 strcat(path, "/");
100 strcat(path, domain);
102 return path;
105 /* XXX: doesn't quite belong to *user*defaults.c */
106 char *wglobaldefaultspathfordomain(const char *domain)
108 char *t = NULL;
109 size_t len;
111 len = strlen(DEFSDATADIR) + strlen(domain) + 2;
112 t = wmalloc(len);
113 snprintf(t, len, "%s/%s", DEFSDATADIR, domain);
115 return t;
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')
128 return;
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 */
151 (void) foo;
153 while (database) {
154 if (!database->dontSync)
155 WMSynchronizeUserDefaults(database);
156 database = database->next;
160 #ifndef HAVE_INOTIFY
161 static void addSynchronizeTimerHandler(void)
163 static Bool initialized = False;
165 if (!initialized) {
166 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL,
167 synchronizeUserDefaults, NULL);
168 initialized = True;
171 #endif
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;
182 char *path;
183 struct stat stbuf;
185 if (!database->path) {
186 path = wdefaultspathfordomain(WMGetApplicationName());
187 release = True;
188 } else {
189 path = database->path;
192 if (stat(path, &stbuf) >= 0 && stbuf.st_mtime > database->timestamp)
193 fileIsNewer = True;
195 if (database->appDomain && (database->dirty || fileIsNewer)) {
196 if (database->dirty && fileIsNewer) {
197 plF = WMReadPropListFromFile(path);
198 if (plF) {
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);
204 notify = True;
205 } else {
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);
214 if (plF) {
215 WMReleasePropList(database->appDomain);
216 database->appDomain = plF;
217 key = database->searchList[0];
218 WMPutInPLDictionary(database->defaults, key, plF);
219 notify = True;
220 } else {
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);
227 database->dirty = 0;
229 if (stat(path, &stbuf) >= 0)
230 database->timestamp = stbuf.st_mtime;
232 if (notify) {
233 WMPostNotificationName(WMUserDefaultsDidChangeNotification, database, NULL);
237 if (release)
238 wfree(path);
242 void WMSaveUserDefaults(WMUserDefaults * database)
244 if (database->appDomain) {
245 struct stat stbuf;
246 char *path;
247 Bool release = False;
249 if (!database->path) {
250 path = wdefaultspathfordomain(WMGetApplicationName());
251 release = True;
252 } else {
253 path = database->path;
255 WMWritePropListToFile(database->appDomain, path);
256 database->dirty = 0;
257 if (stat(path, &stbuf) >= 0)
258 database->timestamp = stbuf.st_mtime;
259 if (release)
260 wfree(path);
264 WMUserDefaults *WMGetStandardUserDefaults(void)
266 WMUserDefaults *defaults;
267 WMPropList *domain;
268 WMPropList *key;
269 struct stat stbuf;
270 char *path;
271 int i;
273 if (sharedUserDefaults) {
274 defaults = sharedUserDefaults;
275 while (defaults) {
276 /* path == NULL only for StandardUserDefaults db */
277 if (defaults->path == NULL)
278 return defaults;
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);
304 if (!domain)
305 domain = WMCreatePLDictionary(NULL, NULL);
307 wfree(path);
309 defaults->appDomain = domain;
311 if (domain)
312 WMPutInPLDictionary(defaults->defaults, key, domain);
314 /* global domain */
315 key = WMCreatePLString("WMGLOBAL");
316 defaults->searchList[1] = key;
318 path = wdefaultspathfordomain(WMGetFromPLString(key));
320 domain = WMReadPropListFromFile(path);
322 wfree(path);
324 if (!domain)
325 domain = WMCreatePLDictionary(NULL, NULL);
327 if (domain)
328 WMPutInPLDictionary(defaults->defaults, key, domain);
330 /* terminate list */
331 defaults->searchList[2] = NULL;
333 defaults->searchListArray = WMCreatePLArray(NULL, NULL);
335 i = 0;
336 while (defaults->searchList[i]) {
337 WMAddToPLArray(defaults->searchListArray, defaults->searchList[i]);
338 i++;
341 if (sharedUserDefaults)
342 defaults->next = sharedUserDefaults;
343 sharedUserDefaults = defaults;
345 #ifndef HAVE_INOTIFY
346 addSynchronizeTimerHandler();
347 #endif
348 registerSaveOnExit();
350 return defaults;
353 WMUserDefaults *WMGetDefaultsFromPath(const char *path)
355 WMUserDefaults *defaults;
356 WMPropList *domain;
357 WMPropList *key;
358 struct stat stbuf;
359 const char *name;
360 int i;
362 assert(path != NULL);
364 if (sharedUserDefaults) {
365 defaults = sharedUserDefaults;
366 while (defaults) {
367 if (defaults->path && strcmp(defaults->path, path) == 0)
368 return defaults;
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, '/');
380 if (!name)
381 name = path;
382 else
383 name++;
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);
393 if (!domain)
394 domain = WMCreatePLDictionary(NULL, NULL);
396 defaults->path = wstrdup(path);
398 defaults->appDomain = domain;
400 if (domain)
401 WMPutInPLDictionary(defaults->defaults, key, domain);
403 /* terminate list */
404 defaults->searchList[1] = NULL;
406 defaults->searchListArray = WMCreatePLArray(NULL, NULL);
408 i = 0;
409 while (defaults->searchList[i]) {
410 WMAddToPLArray(defaults->searchListArray, defaults->searchList[i]);
411 i++;
414 if (sharedUserDefaults)
415 defaults->next = sharedUserDefaults;
416 sharedUserDefaults = defaults;
418 #ifndef HAVE_INOTIFY
419 addSynchronizeTimerHandler();
420 #endif
421 registerSaveOnExit();
423 return defaults;
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);
439 int i = 0;
441 while (database->searchList[i] && !object) {
442 domainName = database->searchList[i];
443 domain = WMGetFromPLDictionary(database->defaults, domainName);
444 if (domain) {
445 object = WMGetFromPLDictionary(domain, key);
447 i++;
449 WMReleasePropList(key);
451 return object;
454 void WMSetUDObjectForKey(WMUserDefaults * database, WMPropList * object, const char *defaultName)
456 WMPropList *key = WMCreatePLString(defaultName);
458 database->dirty = 1;
460 WMPutInPLDictionary(database->appDomain, key, object);
461 WMReleasePropList(key);
464 void WMRemoveUDObjectForKey(WMUserDefaults * database, const char *defaultName)
466 WMPropList *key = WMCreatePLString(defaultName);
468 database->dirty = 1;
470 WMRemoveFromPLDictionary(database->appDomain, key);
472 WMReleasePropList(key);
475 char *WMGetUDStringForKey(WMUserDefaults * database, const char *defaultName)
477 WMPropList *val;
479 val = WMGetUDObjectForKey(database, defaultName);
481 if (!val)
482 return NULL;
484 if (!WMIsPLString(val))
485 return NULL;
487 return WMGetFromPLString(val);
490 int WMGetUDIntegerForKey(WMUserDefaults * database, const char *defaultName)
492 WMPropList *val;
493 char *str;
494 int value;
496 val = WMGetUDObjectForKey(database, defaultName);
498 if (!val)
499 return 0;
501 if (!WMIsPLString(val))
502 return 0;
504 str = WMGetFromPLString(val);
505 if (!str)
506 return 0;
508 if (sscanf(str, "%i", &value) != 1)
509 return 0;
511 return value;
514 float WMGetUDFloatForKey(WMUserDefaults * database, const char *defaultName)
516 WMPropList *val;
517 char *str;
518 float value;
520 val = WMGetUDObjectForKey(database, defaultName);
522 if (!val || !WMIsPLString(val))
523 return 0.0;
525 if (!(str = WMGetFromPLString(val)))
526 return 0.0;
528 if (sscanf(str, "%f", &value) != 1)
529 return 0.0;
531 return value;
534 Bool WMGetUDBoolForKey(WMUserDefaults * database, const char *defaultName)
536 WMPropList *val;
537 int value;
538 char *str;
540 val = WMGetUDObjectForKey(database, defaultName);
542 if (!val)
543 return False;
545 if (!WMIsPLString(val))
546 return False;
548 str = WMGetFromPLString(val);
549 if (!str)
550 return False;
552 if (sscanf(str, "%i", &value) == 1 && value != 0)
553 return True;
555 if (strcasecmp(str, "YES") == 0)
556 return True;
558 if (strcasecmp(str, "Y") == 0)
559 return True;
561 return False;
564 void WMSetUDIntegerForKey(WMUserDefaults * database, int value, const char *defaultName)
566 WMPropList *object;
567 char buffer[128];
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)
578 WMPropList *object;
580 object = WMCreatePLString(value);
582 WMSetUDObjectForKey(database, object, defaultName);
583 WMReleasePropList(object);
586 void WMSetUDFloatForKey(WMUserDefaults * database, float value, const char *defaultName)
588 WMPropList *object;
589 char buffer[128];
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;
602 if (!yes) {
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)
617 int i, c;
619 if (database->searchList) {
620 i = 0;
621 while (database->searchList[i]) {
622 WMReleasePropList(database->searchList[i]);
623 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);