WINGs: fix possible NULL pointer dereference (Coverity #50197)
[wmaker-crm.git] / WINGs / userdefaults.c
blob92f4e0f31bff036706f418b61d9cb75fa6d4c8c2
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 #ifndef GLOBAL_DEFAULTS_SUBDIR
107 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
108 #endif
109 char *wglobaldefaultspathfordomain(const char *domain)
111 char *t = NULL;
112 size_t len;
114 len = strlen( SYSCONFDIR ) + strlen( GLOBAL_DEFAULTS_SUBDIR ) + strlen(domain) + 3;
115 t = wmalloc(len);
116 snprintf(t, len, "%s/%s/%s", SYSCONFDIR, GLOBAL_DEFAULTS_SUBDIR, domain);
118 return t;
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')
131 return;
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 */
154 (void) foo;
156 while (database) {
157 if (!database->dontSync)
158 WMSynchronizeUserDefaults(database);
159 database = database->next;
163 #ifndef HAVE_INOTIFY
164 static void addSynchronizeTimerHandler(void)
166 static Bool initialized = False;
168 if (!initialized) {
169 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL,
170 synchronizeUserDefaults, NULL);
171 initialized = True;
174 #endif
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;
185 char *path;
186 struct stat stbuf;
188 if (!database->path) {
189 path = wdefaultspathfordomain(WMGetApplicationName());
190 release = True;
191 } else {
192 path = database->path;
195 if (stat(path, &stbuf) >= 0 && stbuf.st_mtime > database->timestamp)
196 fileIsNewer = True;
198 if (database->appDomain && (database->dirty || fileIsNewer)) {
199 if (database->dirty && fileIsNewer) {
200 plF = WMReadPropListFromFile(path);
201 if (plF) {
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);
207 notify = True;
208 } else {
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);
217 if (plF) {
218 WMReleasePropList(database->appDomain);
219 database->appDomain = plF;
220 key = database->searchList[0];
221 WMPutInPLDictionary(database->defaults, key, plF);
222 notify = True;
223 } else {
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);
230 database->dirty = 0;
232 if (stat(path, &stbuf) >= 0)
233 database->timestamp = stbuf.st_mtime;
235 if (notify) {
236 WMPostNotificationName(WMUserDefaultsDidChangeNotification, database, NULL);
240 if (release)
241 wfree(path);
245 void WMSaveUserDefaults(WMUserDefaults * database)
247 if (database->appDomain) {
248 struct stat stbuf;
249 char *path;
250 Bool release = False;
252 if (!database->path) {
253 path = wdefaultspathfordomain(WMGetApplicationName());
254 release = True;
255 } else {
256 path = database->path;
258 WMWritePropListToFile(database->appDomain, path);
259 database->dirty = 0;
260 if (stat(path, &stbuf) >= 0)
261 database->timestamp = stbuf.st_mtime;
262 if (release)
263 wfree(path);
267 WMUserDefaults *WMGetStandardUserDefaults(void)
269 WMUserDefaults *defaults;
270 WMPropList *domain;
271 WMPropList *key;
272 struct stat stbuf;
273 char *path;
274 int i;
276 if (sharedUserDefaults) {
277 defaults = sharedUserDefaults;
278 while (defaults) {
279 /* path == NULL only for StandardUserDefaults db */
280 if (defaults->path == NULL)
281 return defaults;
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);
307 if (!domain)
308 domain = WMCreatePLDictionary(NULL, NULL);
310 wfree(path);
312 defaults->appDomain = domain;
314 if (domain)
315 WMPutInPLDictionary(defaults->defaults, key, domain);
317 /* global domain */
318 key = WMCreatePLString("WMGLOBAL");
319 defaults->searchList[1] = key;
321 path = wdefaultspathfordomain(WMGetFromPLString(key));
323 domain = WMReadPropListFromFile(path);
325 wfree(path);
327 if (!domain)
328 domain = WMCreatePLDictionary(NULL, NULL);
330 if (domain)
331 WMPutInPLDictionary(defaults->defaults, key, domain);
333 /* terminate list */
334 defaults->searchList[2] = NULL;
336 defaults->searchListArray = WMCreatePLArray(NULL, NULL);
338 i = 0;
339 while (defaults->searchList[i]) {
340 WMAddToPLArray(defaults->searchListArray, defaults->searchList[i]);
341 i++;
344 if (sharedUserDefaults)
345 defaults->next = sharedUserDefaults;
346 sharedUserDefaults = defaults;
348 #ifndef HAVE_INOTIFY
349 addSynchronizeTimerHandler();
350 #endif
351 registerSaveOnExit();
353 return defaults;
356 WMUserDefaults *WMGetDefaultsFromPath(const char *path)
358 WMUserDefaults *defaults;
359 WMPropList *domain;
360 WMPropList *key;
361 struct stat stbuf;
362 const char *name;
363 int i;
365 assert(path != NULL);
367 if (sharedUserDefaults) {
368 defaults = sharedUserDefaults;
369 while (defaults) {
370 if (defaults->path && strcmp(defaults->path, path) == 0)
371 return defaults;
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, '/');
383 if (!name)
384 name = path;
385 else
386 name++;
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);
396 if (!domain)
397 domain = WMCreatePLDictionary(NULL, NULL);
399 defaults->path = wstrdup(path);
401 defaults->appDomain = domain;
403 if (domain)
404 WMPutInPLDictionary(defaults->defaults, key, domain);
406 /* terminate list */
407 defaults->searchList[1] = NULL;
409 defaults->searchListArray = WMCreatePLArray(NULL, NULL);
411 i = 0;
412 while (defaults->searchList[i]) {
413 WMAddToPLArray(defaults->searchListArray, defaults->searchList[i]);
414 i++;
417 if (sharedUserDefaults)
418 defaults->next = sharedUserDefaults;
419 sharedUserDefaults = defaults;
421 #ifndef HAVE_INOTIFY
422 addSynchronizeTimerHandler();
423 #endif
424 registerSaveOnExit();
426 return defaults;
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);
442 int i = 0;
444 while (database->searchList[i] && !object) {
445 domainName = database->searchList[i];
446 domain = WMGetFromPLDictionary(database->defaults, domainName);
447 if (domain) {
448 object = WMGetFromPLDictionary(domain, key);
450 i++;
452 WMReleasePropList(key);
454 return object;
457 void WMSetUDObjectForKey(WMUserDefaults * database, WMPropList * object, const char *defaultName)
459 WMPropList *key = WMCreatePLString(defaultName);
461 database->dirty = 1;
463 WMPutInPLDictionary(database->appDomain, key, object);
464 WMReleasePropList(key);
467 void WMRemoveUDObjectForKey(WMUserDefaults * database, const char *defaultName)
469 WMPropList *key = WMCreatePLString(defaultName);
471 database->dirty = 1;
473 WMRemoveFromPLDictionary(database->appDomain, key);
475 WMReleasePropList(key);
478 char *WMGetUDStringForKey(WMUserDefaults * database, const char *defaultName)
480 WMPropList *val;
482 val = WMGetUDObjectForKey(database, defaultName);
484 if (!val)
485 return NULL;
487 if (!WMIsPLString(val))
488 return NULL;
490 return WMGetFromPLString(val);
493 int WMGetUDIntegerForKey(WMUserDefaults * database, const char *defaultName)
495 WMPropList *val;
496 char *str;
497 int value;
499 val = WMGetUDObjectForKey(database, defaultName);
501 if (!val)
502 return 0;
504 if (!WMIsPLString(val))
505 return 0;
507 str = WMGetFromPLString(val);
508 if (!str)
509 return 0;
511 if (sscanf(str, "%i", &value) != 1)
512 return 0;
514 return value;
517 float WMGetUDFloatForKey(WMUserDefaults * database, const char *defaultName)
519 WMPropList *val;
520 char *str;
521 float value;
523 val = WMGetUDObjectForKey(database, defaultName);
525 if (!val || !WMIsPLString(val))
526 return 0.0;
528 if (!(str = WMGetFromPLString(val)))
529 return 0.0;
531 if (sscanf(str, "%f", &value) != 1)
532 return 0.0;
534 return value;
537 Bool WMGetUDBoolForKey(WMUserDefaults * database, const char *defaultName)
539 WMPropList *val;
540 int value;
541 char *str;
543 val = WMGetUDObjectForKey(database, defaultName);
545 if (!val)
546 return False;
548 if (!WMIsPLString(val))
549 return False;
551 str = WMGetFromPLString(val);
552 if (!str)
553 return False;
555 if (sscanf(str, "%i", &value) == 1 && value != 0)
556 return True;
558 if (strcasecmp(str, "YES") == 0)
559 return True;
561 if (strcasecmp(str, "Y") == 0)
562 return True;
564 return False;
567 void WMSetUDIntegerForKey(WMUserDefaults * database, int value, const char *defaultName)
569 WMPropList *object;
570 char buffer[128];
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)
581 WMPropList *object;
583 object = WMCreatePLString(value);
585 WMSetUDObjectForKey(database, object, defaultName);
586 WMReleasePropList(object);
589 void WMSetUDFloatForKey(WMUserDefaults * database, float value, const char *defaultName)
591 WMPropList *object;
592 char buffer[128];
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;
605 if (!yes) {
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)
620 int i, c;
622 if (database->searchList) {
623 i = 0;
624 while (database->searchList[i]) {
625 WMReleasePropList(database->searchList[i]);
626 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);