Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / proplist.c
1
2 #include <string.h>
3 #include <stdarg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <ctype.h>
10
11 #include "WUtil.h"
12 #include "wconfig.h"
13
14 typedef enum {
15         WPLString = 0x57504c01,
16         WPLData = 0x57504c02,
17         WPLArray = 0x57504c03,
18         WPLDictionary = 0x57504c04
19 } WPLType;
20
21 typedef struct W_PropList {
22         WPLType type;
23
24         union {
25                 char *string;
26                 WMData *data;
27                 WMArray *array;
28                 WMHashTable *dict;
29         } d;
30
31         int retainCount;
32 } W_PropList;
33
34 typedef struct PLData {
35         char *ptr;
36         int pos;
37         char *filename;
38         int lineNumber;
39 } PLData;
40
41 typedef struct StringBuffer {
42         char *str;
43         int size;
44 } StringBuffer;
45
46 static unsigned hashPropList(WMPropList * plist);
47 static WMPropList *getPLString(PLData * pldata);
48 static WMPropList *getPLQString(PLData * pldata);
49 static WMPropList *getPLData(PLData * pldata);
50 static WMPropList *getPLArray(PLData * pldata);
51 static WMPropList *getPLDictionary(PLData * pldata);
52 static WMPropList *getPropList(PLData * pldata);
53
54 typedef unsigned (*hashFunc) (const void *);
55 typedef Bool(*isEqualFunc) (const void *, const void *);
56 typedef void *(*retainFunc) (const void *);
57 typedef void (*releaseFunc) (const void *);
58
59 static const WMHashTableCallbacks WMPropListHashCallbacks = {
60         (hashFunc) hashPropList,
61         (isEqualFunc) WMIsPropListEqualTo,
62         (retainFunc) NULL,
63         (releaseFunc) NULL
64 };
65
66 static Bool caseSensitive = True;
67
68 #define BUFFERSIZE           8192
69 #define BUFFERSIZE_INCREMENT 1024
70
71 #if 0
72 # define DPUT(s) puts(s)
73 #else
74 # define DPUT(s)
75 #endif
76
77 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
78     (pld)->filename ? "file" : "PropList",\
79     (pld)->filename ? (pld)->filename : "description",\
80     (pld)->lineNumber, msg)
81
82 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
83     || (c)=='+')
84
85 #define CHECK_BUFFER_SIZE(buf, ptr) \
86     if ((ptr) >= (buf).size-1) {\
87     (buf).size += BUFFERSIZE_INCREMENT;\
88     (buf).str = wrealloc((buf).str, (buf).size);\
89     }
90
91 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
92 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
93 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
94 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
95 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
96 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
97 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
98
99 #define MaxHashLength 64
100
101 static unsigned hashPropList(WMPropList * plist)
102 {
103         unsigned ret = 0;
104         unsigned ctr = 0;
105         const char *key;
106         int i, len;
107
108         switch (plist->type) {
109         case WPLString:
110                 key = plist->d.string;
111                 len = WMIN(strlen(key), MaxHashLength);
112                 for (i = 0; i < len; i++) {
113                         ret ^= tolower(key[i]) << ctr;
114                         ctr = (ctr + 1) % sizeof(char *);
115                 }
116                 /*while (*key) {
117                    ret ^= tolower(*key++) << ctr;
118                    ctr = (ctr + 1) % sizeof (char *);
119                    } */
120                 break;
121
122         case WPLData:
123                 key = WMDataBytes(plist->d.data);
124                 len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
125                 for (i = 0; i < len; i++) {
126                         ret ^= key[i] << ctr;
127                         ctr = (ctr + 1) % sizeof(char *);
128                 }
129                 break;
130
131         default:
132                 wwarning(_("Only string or data is supported for a proplist dictionary key"));
133                 wassertrv(False, 0);
134                 break;
135         }
136
137         return ret;
138 }
139
140 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
141 {
142         WMPropList *key, *value;
143         WMHashEnumerator e;
144         int i;
145
146         plist->retainCount += count;
147
148         switch (plist->type) {
149         case WPLString:
150         case WPLData:
151                 break;
152         case WPLArray:
153                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
154                         retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
155                 }
156                 break;
157         case WPLDictionary:
158                 e = WMEnumerateHashTable(plist->d.dict);
159                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
160                         retainPropListByCount(key, count);
161                         retainPropListByCount(value, count);
162                 }
163                 break;
164         default:
165                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
166                 wassertrv(False, NULL);
167                 break;
168         }
169
170         return plist;
171 }
172
173 static void releasePropListByCount(WMPropList * plist, int count)
174 {
175         WMPropList *key, *value;
176         WMHashEnumerator e;
177         int i;
178
179         plist->retainCount -= count;
180
181         switch (plist->type) {
182         case WPLString:
183                 if (plist->retainCount < 1) {
184                         wfree(plist->d.string);
185                         wfree(plist);
186                 }
187                 break;
188         case WPLData:
189                 if (plist->retainCount < 1) {
190                         WMReleaseData(plist->d.data);
191                         wfree(plist);
192                 }
193                 break;
194         case WPLArray:
195                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
196                         releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
197                 }
198                 if (plist->retainCount < 1) {
199                         WMFreeArray(plist->d.array);
200                         wfree(plist);
201                 }
202                 break;
203         case WPLDictionary:
204                 e = WMEnumerateHashTable(plist->d.dict);
205                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
206                         releasePropListByCount(key, count);
207                         releasePropListByCount(value, count);
208                 }
209                 if (plist->retainCount < 1) {
210                         WMFreeHashTable(plist->d.dict);
211                         wfree(plist);
212                 }
213                 break;
214         default:
215                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
216                 wassertr(False);
217                 break;
218         }
219 }
220
221 static char *dataDescription(WMPropList * plist)
222 {
223         const unsigned char *data;
224         char *retVal;
225         int i, j, length;
226
227         data = WMDataBytes(plist->d.data);
228         length = WMGetDataLength(plist->d.data);
229
230         retVal = (char *)wmalloc(2 * length + length / 4 + 3);
231
232         retVal[0] = '<';
233         for (i = 0, j = 1; i < length; i++) {
234                 retVal[j++] = num2char((data[i] >> 4) & 0x0f);
235                 retVal[j++] = num2char(data[i] & 0x0f);
236                 if ((i & 0x03) == 3 && i != length - 1) {
237                         /* if we've just finished a 32-bit int, add a space */
238                         retVal[j++] = ' ';
239                 }
240         }
241         retVal[j++] = '>';
242         retVal[j] = '\0';
243
244         return retVal;
245 }
246
247 static char *stringDescription(WMPropList * plist)
248 {
249         const char *str;
250         char *retVal, *sPtr, *dPtr;
251         int len, quote;
252         unsigned char ch;
253
254         str = plist->d.string;
255
256         if (strlen(str) == 0) {
257                 return wstrdup("\"\"");
258         }
259
260         /* FIXME: make this work with unichars. */
261
262         quote = 0;
263         sPtr = (char *)str;
264         len = 0;
265         while ((ch = *sPtr)) {
266                 if (!noquote(ch)) {
267                         quote = 1;
268                         if (charesc(ch))
269                                 len++;
270                         else if (numesc(ch))
271                                 len += 3;
272                 }
273                 sPtr++;
274                 len++;
275         }
276
277         if (quote)
278                 len += 2;
279
280         retVal = (char *)wmalloc(len + 1);
281
282         sPtr = (char *)str;
283         dPtr = retVal;
284
285         if (quote)
286                 *dPtr++ = '"';
287
288         while ((ch = *sPtr)) {
289                 if (charesc(ch)) {
290                         *(dPtr++) = '\\';
291                         switch (ch) {
292                         case '\a':
293                                 *dPtr = 'a';
294                                 break;
295                         case '\b':
296                                 *dPtr = 'b';
297                                 break;
298                         case '\t':
299                                 *dPtr = 't';
300                                 break;
301                         case '\n':
302                                 *dPtr = 'n';
303                                 break;
304                         case '\v':
305                                 *dPtr = 'v';
306                                 break;
307                         case '\f':
308                                 *dPtr = 'f';
309                                 break;
310                         default:
311                                 *dPtr = ch;     /* " or \ */
312                         }
313                 } else if (numesc(ch)) {
314                         *(dPtr++) = '\\';
315                         *(dPtr++) = '0' + ((ch >> 6) & 07);
316                         *(dPtr++) = '0' + ((ch >> 3) & 07);
317                         *dPtr = '0' + (ch & 07);
318                 } else {
319                         *dPtr = ch;
320                 }
321                 sPtr++;
322                 dPtr++;
323         }
324
325         if (quote)
326                 *dPtr++ = '"';
327
328         *dPtr = '\0';
329
330         return retVal;
331 }
332
333 static char *description(WMPropList * plist)
334 {
335         WMPropList *key, *val;
336         char *retstr = NULL;
337         char *str, *tmp, *skey, *sval;
338         WMHashEnumerator e;
339         int i;
340
341         switch (plist->type) {
342         case WPLString:
343                 retstr = stringDescription(plist);
344                 break;
345         case WPLData:
346                 retstr = dataDescription(plist);
347                 break;
348         case WPLArray:
349                 retstr = wstrdup("(");
350                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
351                         str = description(WMGetFromArray(plist->d.array, i));
352                         if (i == 0) {
353                                 retstr = wstrappend(retstr, str);
354                         } else {
355                                 tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
356                                 sprintf(tmp, "%s, %s", retstr, str);
357                                 wfree(retstr);
358                                 retstr = tmp;
359                         }
360                         wfree(str);
361                 }
362                 retstr = wstrappend(retstr, ")");
363                 break;
364         case WPLDictionary:
365                 retstr = wstrdup("{");
366                 e = WMEnumerateHashTable(plist->d.dict);
367                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
368                         skey = description(key);
369                         sval = description(val);
370                         tmp = (char *)wmalloc(strlen(retstr) + strlen(skey) + strlen(sval) + 5);
371                         sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
372                         wfree(skey);
373                         wfree(sval);
374                         wfree(retstr);
375                         retstr = tmp;
376                 }
377                 retstr = wstrappend(retstr, "}");
378                 break;
379         default:
380                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
381                 wassertrv(False, NULL);
382                 break;
383         }
384
385         return retstr;
386 }
387
388 static char *indentedDescription(WMPropList * plist, int level)
389 {
390         WMPropList *key, *val;
391         char *retstr = NULL;
392         char *str, *tmp, *skey, *sval;
393         WMHashEnumerator e;
394         int i;
395
396         if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
397                 retstr = description(plist);
398
399                 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
400                         return retstr;
401                 } else if (retstr) {
402                         wfree(retstr);
403                         retstr = NULL;
404                 }
405         }
406
407         switch (plist->type) {
408         case WPLString:
409                 retstr = stringDescription(plist);
410                 break;
411         case WPLData:
412                 retstr = dataDescription(plist);
413                 break;
414         case WPLArray:
415                 retstr = wstrdup("(\n");
416                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
417                         str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
418                         if (i == 0) {
419                                 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
420                                 sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
421                                 wfree(retstr);
422                                 retstr = tmp;
423                         } else {
424                                 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
425                                 sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
426                                 wfree(retstr);
427                                 retstr = tmp;
428                         }
429                         wfree(str);
430                 }
431                 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
432                 sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
433                 wfree(retstr);
434                 retstr = tmp;
435                 break;
436         case WPLDictionary:
437                 retstr = wstrdup("{\n");
438                 e = WMEnumerateHashTable(plist->d.dict);
439                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
440                         skey = indentedDescription(key, level + 1);
441                         sval = indentedDescription(val, level + 1);
442                         tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(skey)
443                                               + strlen(sval) + 6);
444                         sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
445                         wfree(skey);
446                         wfree(sval);
447                         wfree(retstr);
448                         retstr = tmp;
449                 }
450                 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
451                 sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
452                 wfree(retstr);
453                 retstr = tmp;
454                 break;
455         default:
456                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
457                 wassertrv(False, NULL);
458                 break;
459         }
460
461         return retstr;
462 }
463
464 static INLINE int getChar(PLData * pldata)
465 {
466         int c;
467
468         c = pldata->ptr[pldata->pos];
469         if (c == 0) {
470                 return 0;
471         }
472
473         pldata->pos++;
474
475         if (c == '\n')
476                 pldata->lineNumber++;
477
478         return c;
479 }
480
481 static INLINE int getNonSpaceChar(PLData * pldata)
482 {
483         int c;
484
485         while (1) {
486                 c = pldata->ptr[pldata->pos];
487                 if (c == 0) {
488                         break;
489                 }
490                 pldata->pos++;
491                 if (c == '\n') {
492                         pldata->lineNumber++;
493                 } else if (!isspace(c)) {
494                         break;
495                 }
496         }
497
498         return c;
499 }
500
501 static char *unescapestr(char *src)
502 {
503         char *dest = wmalloc(strlen(src) + 1);
504         char *sPtr, *dPtr;
505         char ch;
506
507         for (sPtr = src, dPtr = dest; *sPtr; sPtr++, dPtr++) {
508                 if (*sPtr != '\\') {
509                         *dPtr = *sPtr;
510                 } else {
511                         ch = *(++sPtr);
512                         if ((ch >= '0') && (ch <= '3')) {
513                                 /* assume next 2 chars are octal too */
514                                 *dPtr = ((ch & 07) << 6);
515                                 *dPtr |= ((*(++sPtr) & 07) << 3);
516                                 *dPtr |= *(++sPtr) & 07;
517                         } else {
518                                 switch (ch) {
519                                 case 'a':
520                                         *dPtr = '\a';
521                                         break;
522                                 case 'b':
523                                         *dPtr = '\b';
524                                         break;
525                                 case 't':
526                                         *dPtr = '\t';
527                                         break;
528                                 case 'r':
529                                         *dPtr = '\r';
530                                         break;
531                                 case 'n':
532                                         *dPtr = '\n';
533                                         break;
534                                 case 'v':
535                                         *dPtr = '\v';
536                                         break;
537                                 case 'f':
538                                         *dPtr = '\f';
539                                         break;
540                                 default:
541                                         *dPtr = *sPtr;
542                                 }
543                         }
544                 }
545         }
546
547         *dPtr = 0;
548
549         return dest;
550 }
551
552 static WMPropList *getPLString(PLData * pldata)
553 {
554         WMPropList *plist;
555         StringBuffer sBuf;
556         int ptr = 0;
557         int c;
558
559         sBuf.str = wmalloc(BUFFERSIZE);
560         sBuf.size = BUFFERSIZE;
561
562         while (1) {
563                 c = getChar(pldata);
564                 if (ISSTRINGABLE(c)) {
565                         CHECK_BUFFER_SIZE(sBuf, ptr);
566                         sBuf.str[ptr++] = c;
567                 } else {
568                         if (c != 0) {
569                                 pldata->pos--;
570                         }
571                         break;
572                 }
573         }
574
575         sBuf.str[ptr] = 0;
576
577         if (ptr == 0) {
578                 plist = NULL;
579         } else {
580                 char *tmp = unescapestr(sBuf.str);
581                 plist = WMCreatePLString(tmp);
582                 wfree(tmp);
583         }
584
585         wfree(sBuf.str);
586
587         return plist;
588 }
589
590 static WMPropList *getPLQString(PLData * pldata)
591 {
592         WMPropList *plist;
593         int ptr = 0, escaping = 0, ok = 1;
594         int c;
595         StringBuffer sBuf;
596
597         sBuf.str = wmalloc(BUFFERSIZE);
598         sBuf.size = BUFFERSIZE;
599
600         while (1) {
601                 c = getChar(pldata);
602                 if (!escaping) {
603                         if (c == '\\') {
604                                 escaping = 1;
605                                 continue;
606                         } else if (c == '"') {
607                                 break;
608                         }
609                 } else {
610                         CHECK_BUFFER_SIZE(sBuf, ptr);
611                         sBuf.str[ptr++] = '\\';
612                         escaping = 0;
613                 }
614
615                 if (c == 0) {
616                         COMPLAIN(pldata, _("unterminated PropList string"));
617                         ok = 0;
618                         break;
619                 } else {
620                         CHECK_BUFFER_SIZE(sBuf, ptr);
621                         sBuf.str[ptr++] = c;
622                 }
623         }
624
625         sBuf.str[ptr] = 0;
626
627         if (!ok) {
628                 plist = NULL;
629         } else {
630                 char *tmp = unescapestr(sBuf.str);
631                 plist = WMCreatePLString(tmp);
632                 wfree(tmp);
633         }
634
635         wfree(sBuf.str);
636
637         return plist;
638 }
639
640 static WMPropList *getPLData(PLData * pldata)
641 {
642         int ok = 1;
643         int len = 0;
644         int c1, c2;
645         unsigned char buf[BUFFERSIZE], byte;
646         WMPropList *plist;
647         WMData *data;
648
649         data = WMCreateDataWithCapacity(0);
650
651         while (1) {
652                 c1 = getNonSpaceChar(pldata);
653                 if (c1 == 0) {
654                         COMPLAIN(pldata, _("unterminated PropList data"));
655                         ok = 0;
656                         break;
657                 } else if (c1 == '>') {
658                         break;
659                 } else if (ishexdigit(c1)) {
660                         c2 = getNonSpaceChar(pldata);
661                         if (c2 == 0 || c2 == '>') {
662                                 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
663                                 ok = 0;
664                                 break;
665                         } else if (ishexdigit(c2)) {
666                                 byte = char2num(c1) << 4;
667                                 byte |= char2num(c2);
668                                 buf[len++] = byte;
669                                 if (len == sizeof(buf)) {
670                                         WMAppendDataBytes(data, buf, len);
671                                         len = 0;
672                                 }
673                         } else {
674                                 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
675                                 ok = 0;
676                                 break;
677                         }
678                 } else {
679                         COMPLAIN(pldata, _("non hexdigit character in PropList data"));
680                         ok = 0;
681                         break;
682                 }
683         }
684
685         if (!ok) {
686                 WMReleaseData(data);
687                 return NULL;
688         }
689
690         if (len > 0)
691                 WMAppendDataBytes(data, buf, len);
692
693         plist = WMCreatePLData(data);
694         WMReleaseData(data);
695
696         return plist;
697 }
698
699 static WMPropList *getPLArray(PLData * pldata)
700 {
701         Bool first = True;
702         int ok = 1;
703         int c;
704         WMPropList *array, *obj;
705
706         array = WMCreatePLArray(NULL);
707
708         while (1) {
709                 c = getNonSpaceChar(pldata);
710                 if (c == 0) {
711                         COMPLAIN(pldata, _("unterminated PropList array"));
712                         ok = 0;
713                         break;
714                 } else if (c == ')') {
715                         break;
716                 } else if (c == ',') {
717                         /* continue normally */
718                 } else if (!first) {
719                         COMPLAIN(pldata, _("missing or unterminated PropList array"));
720                         ok = 0;
721                         break;
722                 } else {
723                         pldata->pos--;
724                 }
725                 first = False;
726
727                 obj = getPropList(pldata);
728                 if (!obj) {
729                         COMPLAIN(pldata, _("could not get PropList array element"));
730                         ok = 0;
731                         break;
732                 }
733                 WMAddToPLArray(array, obj);
734                 WMReleasePropList(obj);
735         }
736
737         if (!ok) {
738                 WMReleasePropList(array);
739                 array = NULL;
740         }
741
742         return array;
743 }
744
745 static WMPropList *getPLDictionary(PLData * pldata)
746 {
747         int ok = 1;
748         int c;
749         WMPropList *dict, *key, *value;
750
751         dict = WMCreatePLDictionary(NULL, NULL);
752
753         while (1) {
754                 c = getNonSpaceChar(pldata);
755                 if (c == 0) {
756                         COMPLAIN(pldata, _("unterminated PropList dictionary"));
757                         ok = 0;
758                         break;
759                 } else if (c == '}') {
760                         break;
761                 }
762
763                 DPUT("getting PropList dictionary key");
764                 if (c == '<') {
765                         key = getPLData(pldata);
766                 } else if (c == '"') {
767                         key = getPLQString(pldata);
768                 } else if (ISSTRINGABLE(c)) {
769                         pldata->pos--;
770                         key = getPLString(pldata);
771                 } else {
772                         if (c == '=') {
773                                 COMPLAIN(pldata, _("missing PropList dictionary key"));
774                         } else {
775                                 COMPLAIN(pldata, _("missing PropList dictionary entry key "
776                                                    "or unterminated dictionary"));
777                         }
778                         ok = 0;
779                         break;
780                 }
781
782                 if (!key) {
783                         COMPLAIN(pldata, _("error parsing PropList dictionary key"));
784                         ok = 0;
785                         break;
786                 }
787
788                 c = getNonSpaceChar(pldata);
789                 if (c != '=') {
790                         WMReleasePropList(key);
791                         COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
792                         ok = 0;
793                         break;
794                 }
795
796                 DPUT("getting PropList dictionary entry value for key");
797                 value = getPropList(pldata);
798                 if (!value) {
799                         COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
800                         WMReleasePropList(key);
801                         ok = 0;
802                         break;
803                 }
804
805                 c = getNonSpaceChar(pldata);
806                 if (c != ';') {
807                         COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
808                         WMReleasePropList(key);
809                         WMReleasePropList(value);
810                         ok = 0;
811                         break;
812                 }
813
814                 WMPutInPLDictionary(dict, key, value);
815                 WMReleasePropList(key);
816                 WMReleasePropList(value);
817         }
818
819         if (!ok) {
820                 WMReleasePropList(dict);
821                 dict = NULL;
822         }
823
824         return dict;
825 }
826
827 static WMPropList *getPropList(PLData * pldata)
828 {
829         WMPropList *plist;
830         int c;
831
832         c = getNonSpaceChar(pldata);
833
834         switch (c) {
835         case 0:
836                 DPUT("End of PropList");
837                 plist = NULL;
838                 break;
839
840         case '{':
841                 DPUT("Getting PropList dictionary");
842                 plist = getPLDictionary(pldata);
843                 break;
844
845         case '(':
846                 DPUT("Getting PropList array");
847                 plist = getPLArray(pldata);
848                 break;
849
850         case '<':
851                 DPUT("Getting PropList data");
852                 plist = getPLData(pldata);
853                 break;
854
855         case '"':
856                 DPUT("Getting PropList quoted string");
857                 plist = getPLQString(pldata);
858                 break;
859
860         default:
861                 if (ISSTRINGABLE(c)) {
862                         DPUT("Getting PropList string");
863                         pldata->pos--;
864                         plist = getPLString(pldata);
865                 } else {
866                         COMPLAIN(pldata, _("was expecting a string, data, array or "
867                                            "dictionary. If it's a string, try enclosing " "it with \"."));
868                         if (c == '#' || c == '/') {
869                                 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
870                         }
871                         plist = NULL;
872                 }
873                 break;
874         }
875
876         return plist;
877 }
878
879 void WMPLSetCaseSensitive(Bool caseSensitiveness)
880 {
881         caseSensitive = caseSensitiveness;
882 }
883
884 WMPropList *WMCreatePLString(char *str)
885 {
886         WMPropList *plist;
887
888         wassertrv(str != NULL, NULL);
889
890         plist = (WMPropList *) wmalloc(sizeof(W_PropList));
891
892         plist->type = WPLString;
893         plist->d.string = wstrdup(str);
894         plist->retainCount = 1;
895
896         return plist;
897 }
898
899 WMPropList *WMCreatePLData(WMData * data)
900 {
901         WMPropList *plist;
902
903         wassertrv(data != NULL, NULL);
904
905         plist = (WMPropList *) wmalloc(sizeof(W_PropList));
906
907         plist->type = WPLData;
908         plist->d.data = WMRetainData(data);
909         plist->retainCount = 1;
910
911         return plist;
912 }
913
914 WMPropList *WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
915 {
916         WMPropList *plist;
917
918         wassertrv(bytes != NULL, NULL);
919
920         plist = (WMPropList *) wmalloc(sizeof(W_PropList));
921
922         plist->type = WPLData;
923         plist->d.data = WMCreateDataWithBytes(bytes, length);
924         plist->retainCount = 1;
925
926         return plist;
927 }
928
929 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
930 {
931         WMPropList *plist;
932
933         wassertrv(bytes != NULL, NULL);
934
935         plist = (WMPropList *) wmalloc(sizeof(W_PropList));
936
937         plist->type = WPLData;
938         plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
939         plist->retainCount = 1;
940
941         return plist;
942 }
943
944 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
945 {
946         WMPropList *plist, *nelem;
947         va_list ap;
948
949         plist = (WMPropList *) wmalloc(sizeof(W_PropList));
950         plist->type = WPLArray;
951         plist->d.array = WMCreateArray(4);
952         plist->retainCount = 1;
953
954         if (!elem)
955                 return plist;
956
957         WMAddToArray(plist->d.array, WMRetainPropList(elem));
958
959         va_start(ap, elem);
960
961         while (1) {
962                 nelem = va_arg(ap, WMPropList *);
963                 if (!nelem) {
964                         va_end(ap);
965                         return plist;
966                 }
967                 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
968         }
969 }
970
971 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
972 {
973         WMPropList *plist, *nkey, *nvalue, *k, *v;
974         va_list ap;
975
976         plist = (WMPropList *) wmalloc(sizeof(W_PropList));
977         plist->type = WPLDictionary;
978         plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
979         plist->retainCount = 1;
980
981         if (!key || !value)
982                 return plist;
983
984         WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
985
986         va_start(ap, value);
987
988         while (1) {
989                 nkey = va_arg(ap, WMPropList *);
990                 if (!nkey) {
991                         va_end(ap);
992                         return plist;
993                 }
994                 nvalue = va_arg(ap, WMPropList *);
995                 if (!nvalue) {
996                         va_end(ap);
997                         return plist;
998                 }
999                 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
1000                         WMHashRemove(plist->d.dict, k);
1001                         WMReleasePropList(k);
1002                         WMReleasePropList(v);
1003                 }
1004                 WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
1005         }
1006 }
1007
1008 WMPropList *WMRetainPropList(WMPropList * plist)
1009 {
1010         WMPropList *key, *value;
1011         WMHashEnumerator e;
1012         int i;
1013
1014         plist->retainCount++;
1015
1016         switch (plist->type) {
1017         case WPLString:
1018         case WPLData:
1019                 break;
1020         case WPLArray:
1021                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1022                         WMRetainPropList(WMGetFromArray(plist->d.array, i));
1023                 }
1024                 break;
1025         case WPLDictionary:
1026                 e = WMEnumerateHashTable(plist->d.dict);
1027                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1028                         WMRetainPropList(key);
1029                         WMRetainPropList(value);
1030                 }
1031                 break;
1032         default:
1033                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1034                 wassertrv(False, NULL);
1035                 break;
1036         }
1037
1038         return plist;
1039 }
1040
1041 void WMReleasePropList(WMPropList * plist)
1042 {
1043         WMPropList *key, *value;
1044         WMHashEnumerator e;
1045         int i;
1046
1047         plist->retainCount--;
1048
1049         switch (plist->type) {
1050         case WPLString:
1051                 if (plist->retainCount < 1) {
1052                         wfree(plist->d.string);
1053                         wfree(plist);
1054                 }
1055                 break;
1056         case WPLData:
1057                 if (plist->retainCount < 1) {
1058                         WMReleaseData(plist->d.data);
1059                         wfree(plist);
1060                 }
1061                 break;
1062         case WPLArray:
1063                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1064                         WMReleasePropList(WMGetFromArray(plist->d.array, i));
1065                 }
1066                 if (plist->retainCount < 1) {
1067                         WMFreeArray(plist->d.array);
1068                         wfree(plist);
1069                 }
1070                 break;
1071         case WPLDictionary:
1072                 e = WMEnumerateHashTable(plist->d.dict);
1073                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1074                         WMReleasePropList(key);
1075                         WMReleasePropList(value);
1076                 }
1077                 if (plist->retainCount < 1) {
1078                         WMFreeHashTable(plist->d.dict);
1079                         wfree(plist);
1080                 }
1081                 break;
1082         default:
1083                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1084                 wassertr(False);
1085                 break;
1086         }
1087 }
1088
1089 void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
1090 {
1091         wassertr(plist->type == WPLArray);
1092
1093         retainPropListByCount(item, plist->retainCount);
1094         WMInsertInArray(plist->d.array, index, item);
1095 }
1096
1097 void WMAddToPLArray(WMPropList * plist, WMPropList * item)
1098 {
1099         wassertr(plist->type == WPLArray);
1100
1101         retainPropListByCount(item, plist->retainCount);
1102         WMAddToArray(plist->d.array, item);
1103 }
1104
1105 void WMDeleteFromPLArray(WMPropList * plist, int index)
1106 {
1107         WMPropList *item;
1108
1109         wassertr(plist->type == WPLArray);
1110
1111         item = WMGetFromArray(plist->d.array, index);
1112         if (item != NULL) {
1113                 WMDeleteFromArray(plist->d.array, index);
1114                 releasePropListByCount(item, plist->retainCount);
1115         }
1116 }
1117
1118 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1119 {
1120         WMPropList *iPtr;
1121         int i;
1122
1123         wassertr(plist->type == WPLArray);
1124
1125         for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1126                 iPtr = WMGetFromArray(plist->d.array, i);
1127                 if (WMIsPropListEqualTo(item, iPtr)) {
1128                         WMDeleteFromArray(plist->d.array, i);
1129                         releasePropListByCount(iPtr, plist->retainCount);
1130                         break;
1131                 }
1132         }
1133 }
1134
1135 void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
1136 {
1137         wassertr(plist->type == WPLDictionary);
1138
1139         /*WMRetainPropList(key); */
1140         WMRemoveFromPLDictionary(plist, key);
1141         retainPropListByCount(key, plist->retainCount);
1142         retainPropListByCount(value, plist->retainCount);
1143         WMHashInsert(plist->d.dict, key, value);
1144         /*WMReleasePropList(key); */
1145 }
1146
1147 void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
1148 {
1149         WMPropList *k, *v;
1150
1151         wassertr(plist->type == WPLDictionary);
1152
1153         if (WMHashGetItemAndKey(plist->d.dict, key, (void **)&v, (void **)&k)) {
1154                 WMHashRemove(plist->d.dict, k);
1155                 releasePropListByCount(k, plist->retainCount);
1156                 releasePropListByCount(v, plist->retainCount);
1157         }
1158 }
1159
1160 WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1161 {
1162         WMPropList *key, *value, *dvalue;
1163         WMHashEnumerator e;
1164
1165         wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1166
1167         if (source == dest)
1168                 return dest;
1169
1170         e = WMEnumerateHashTable(source->d.dict);
1171         while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1172                 if (recursive && value->type == WPLDictionary) {
1173                         dvalue = WMHashGet(dest->d.dict, key);
1174                         if (dvalue && dvalue->type == WPLDictionary) {
1175                                 WMMergePLDictionaries(dvalue, value, True);
1176                         } else {
1177                                 WMPutInPLDictionary(dest, key, value);
1178                         }
1179                 } else {
1180                         WMPutInPLDictionary(dest, key, value);
1181                 }
1182         }
1183
1184         return dest;
1185 }
1186
1187 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1188 {
1189         WMPropList *key, *value, *dvalue;
1190         WMHashEnumerator e;
1191
1192         wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1193
1194         if (source == dest) {
1195                 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1196                 int i;
1197
1198                 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1199                         WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1200                 }
1201                 return dest;
1202         }
1203
1204         e = WMEnumerateHashTable(source->d.dict);
1205         while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1206                 dvalue = WMHashGet(dest->d.dict, key);
1207                 if (!dvalue)
1208                         continue;
1209                 if (WMIsPropListEqualTo(value, dvalue)) {
1210                         WMRemoveFromPLDictionary(dest, key);
1211                 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1212                         WMSubtractPLDictionaries(dvalue, value, True);
1213                 }
1214         }
1215
1216         return dest;
1217 }
1218
1219 int WMGetPropListItemCount(WMPropList * plist)
1220 {
1221         switch (plist->type) {
1222         case WPLString:
1223         case WPLData:
1224                 return 0;       /* should this be 1 instead? */
1225         case WPLArray:
1226                 return WMGetArrayItemCount(plist->d.array);
1227         case WPLDictionary:
1228                 return (int)WMCountHashTable(plist->d.dict);
1229         default:
1230                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1231                 wassertrv(False, 0);
1232                 break;
1233         }
1234
1235         return 0;
1236 }
1237
1238 Bool WMIsPLString(WMPropList * plist)
1239 {
1240         return (plist->type == WPLString);
1241 }
1242
1243 Bool WMIsPLData(WMPropList * plist)
1244 {
1245         return (plist->type == WPLData);
1246 }
1247
1248 Bool WMIsPLArray(WMPropList * plist)
1249 {
1250         return (plist->type == WPLArray);
1251 }
1252
1253 Bool WMIsPLDictionary(WMPropList * plist)
1254 {
1255         return (plist->type == WPLDictionary);
1256 }
1257
1258 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1259 {
1260         WMPropList *key1, *item1, *item2;
1261         WMHashEnumerator enumerator;
1262         int n, i;
1263
1264         if (plist->type != other->type)
1265                 return False;
1266
1267         switch (plist->type) {
1268         case WPLString:
1269                 if (caseSensitive) {
1270                         return (strcmp(plist->d.string, other->d.string) == 0);
1271                 } else {
1272                         return (strcasecmp(plist->d.string, other->d.string) == 0);
1273                 }
1274         case WPLData:
1275                 return WMIsDataEqualToData(plist->d.data, other->d.data);
1276         case WPLArray:
1277                 n = WMGetArrayItemCount(plist->d.array);
1278                 if (n != WMGetArrayItemCount(other->d.array))
1279                         return False;
1280                 for (i = 0; i < n; i++) {
1281                         item1 = WMGetFromArray(plist->d.array, i);
1282                         item2 = WMGetFromArray(other->d.array, i);
1283                         if (!WMIsPropListEqualTo(item1, item2))
1284                                 return False;
1285                 }
1286                 return True;
1287         case WPLDictionary:
1288                 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1289                         return False;
1290                 enumerator = WMEnumerateHashTable(plist->d.dict);
1291                 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1292                         item2 = WMHashGet(other->d.dict, key1);
1293                         if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1294                                 return False;
1295                 }
1296                 return True;
1297         default:
1298                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1299                 wassertrv(False, False);
1300                 break;
1301         }
1302
1303         return False;
1304 }
1305
1306 char *WMGetFromPLString(WMPropList * plist)
1307 {
1308         wassertrv(plist->type == WPLString, NULL);
1309
1310         return plist->d.string;
1311 }
1312
1313 WMData *WMGetFromPLData(WMPropList * plist)
1314 {
1315         wassertrv(plist->type == WPLData, NULL);
1316
1317         return plist->d.data;
1318 }
1319
1320 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1321 {
1322         wassertrv(plist->type == WPLData, NULL);
1323
1324         return WMDataBytes(plist->d.data);
1325 }
1326
1327 int WMGetPLDataLength(WMPropList * plist)
1328 {
1329         wassertrv(plist->type == WPLData, 0);
1330
1331         return WMGetDataLength(plist->d.data);
1332 }
1333
1334 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1335 {
1336         wassertrv(plist->type == WPLArray, NULL);
1337
1338         return WMGetFromArray(plist->d.array, index);
1339 }
1340
1341 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1342 {
1343         wassertrv(plist->type == WPLDictionary, NULL);
1344
1345         return WMHashGet(plist->d.dict, key);
1346 }
1347
1348 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1349 {
1350         WMPropList *array, *key;
1351         WMHashEnumerator enumerator;
1352
1353         wassertrv(plist->type == WPLDictionary, NULL);
1354
1355         array = (WMPropList *) wmalloc(sizeof(W_PropList));
1356         array->type = WPLArray;
1357         array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1358         array->retainCount = 1;
1359
1360         enumerator = WMEnumerateHashTable(plist->d.dict);
1361         while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1362                 WMAddToArray(array->d.array, WMRetainPropList(key));
1363         }
1364
1365         return array;
1366 }
1367
1368 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1369 {
1370         WMPropList *ret = NULL;
1371         WMPropList *key, *item;
1372         WMHashEnumerator e;
1373         WMData *data;
1374         int i;
1375
1376         switch (plist->type) {
1377         case WPLString:
1378                 ret = WMCreatePLString(plist->d.string);
1379                 break;
1380         case WPLData:
1381                 data = WMCreateDataWithData(plist->d.data);
1382                 ret = WMCreatePLData(data);
1383                 WMReleaseData(data);
1384                 break;
1385         case WPLArray:
1386                 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1387                 ret->type = WPLArray;
1388                 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1389                 ret->retainCount = 1;
1390
1391                 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1392                         WMRetainPropList(WMGetFromArray(ret->d.array, i));
1393
1394                 break;
1395         case WPLDictionary:
1396                 ret = WMCreatePLDictionary(NULL, NULL);
1397                 e = WMEnumerateHashTable(plist->d.dict);
1398                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1399                         WMPutInPLDictionary(ret, key, item);
1400                 }
1401                 break;
1402         default:
1403                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1404                 wassertrv(False, NULL);
1405                 break;
1406         }
1407
1408         return ret;
1409 }
1410
1411 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1412 {
1413         WMPropList *ret = NULL;
1414         WMPropList *key, *item;
1415         WMHashEnumerator e;
1416         WMData *data;
1417         int i;
1418
1419         switch (plist->type) {
1420         case WPLString:
1421                 ret = WMCreatePLString(plist->d.string);
1422                 break;
1423         case WPLData:
1424                 data = WMCreateDataWithData(plist->d.data);
1425                 ret = WMCreatePLData(data);
1426                 WMReleaseData(data);
1427                 break;
1428         case WPLArray:
1429                 ret = WMCreatePLArray(NULL);
1430                 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1431                         item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1432                         WMAddToArray(ret->d.array, item);
1433                 }
1434                 break;
1435         case WPLDictionary:
1436                 ret = WMCreatePLDictionary(NULL, NULL);
1437                 e = WMEnumerateHashTable(plist->d.dict);
1438                 /* While we copy an existing dictionary there is no way that we can
1439                  * have duplicate keys, so we don't need to first remove a key/value
1440                  * pair before inserting the new key/value.
1441                  */
1442                 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1443                         WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1444                 }
1445                 break;
1446         default:
1447                 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1448                 wassertrv(False, NULL);
1449                 break;
1450         }
1451
1452         return ret;
1453 }
1454
1455 WMPropList *WMCreatePropListFromDescription(char *desc)
1456 {
1457         WMPropList *plist = NULL;
1458         PLData *pldata;
1459
1460         pldata = (PLData *) wmalloc(sizeof(PLData));
1461         memset(pldata, 0, sizeof(PLData));
1462         pldata->ptr = desc;
1463         pldata->lineNumber = 1;
1464
1465         plist = getPropList(pldata);
1466
1467         if (getNonSpaceChar(pldata) != 0 && plist) {
1468                 COMPLAIN(pldata, _("extra data after end of property list"));
1469                 /*
1470                  * We can't just ignore garbage after the end of the description
1471                  * (especially if the description was read from a file), because
1472                  * the "garbage" can be the real data and the real garbage is in
1473                  * fact in the beginning of the file (which is now inside plist)
1474                  */
1475                 WMReleasePropList(plist);
1476                 plist = NULL;
1477         }
1478
1479         wfree(pldata);
1480
1481         return plist;
1482 }
1483
1484 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1485 {
1486         return (indented ? indentedDescription(plist, 0) : description(plist));
1487 }
1488
1489 WMPropList *WMReadPropListFromFile(char *file)
1490 {
1491         WMPropList *plist = NULL;
1492         PLData *pldata;
1493         FILE *f;
1494         struct stat stbuf;
1495         size_t length;
1496
1497         f = fopen(file, "rb");
1498         if (!f) {
1499                 /* let the user print the error message if he really needs to */
1500                 /*wsyserror(_("could not open domain file '%s' for reading"), file); */
1501                 return NULL;
1502         }
1503
1504         if (stat(file, &stbuf) == 0) {
1505                 length = (size_t) stbuf.st_size;
1506         } else {
1507                 wsyserror(_("could not get size for file '%s'"), file);
1508                 fclose(f);
1509                 return NULL;
1510         }
1511
1512         pldata = (PLData *) wmalloc(sizeof(PLData));
1513         memset(pldata, 0, sizeof(PLData));
1514         pldata->ptr = (char *)wmalloc(length + 1);
1515         pldata->filename = file;
1516         pldata->lineNumber = 1;
1517
1518         if (fread(pldata->ptr, length, 1, f) != 1) {
1519                 if (ferror(f)) {
1520                         wsyserror(_("error reading from file '%s'"), file);
1521                 }
1522                 plist = NULL;
1523                 goto cleanup;
1524         }
1525
1526         pldata->ptr[length] = 0;
1527
1528         plist = getPropList(pldata);
1529
1530         if (getNonSpaceChar(pldata) != 0 && plist) {
1531                 COMPLAIN(pldata, _("extra data after end of property list"));
1532                 /*
1533                  * We can't just ignore garbage after the end of the description
1534                  * (especially if the description was read from a file), because
1535                  * the "garbage" can be the real data and the real garbage is in
1536                  * fact in the beginning of the file (which is now inside plist)
1537                  */
1538                 WMReleasePropList(plist);
1539                 plist = NULL;
1540         }
1541
1542  cleanup:
1543         wfree(pldata->ptr);
1544         wfree(pldata);
1545         fclose(f);
1546
1547         return plist;
1548 }
1549
1550 /* TODO: review this function's code */
1551
1552 Bool WMWritePropListToFile(WMPropList * plist, char *path, Bool atomically)
1553 {
1554         char *thePath = NULL;
1555         char *desc;
1556         FILE *theFile;
1557
1558         if (atomically) {
1559 #ifdef  HAVE_MKSTEMP
1560                 int fd, mask;
1561 #endif
1562
1563                 /* Use the path name of the destination file as a prefix for the
1564                  * mkstemp() call so that we can be sure that both files are on
1565                  * the same filesystem and the subsequent rename() will work. */
1566                 thePath = wstrconcat(path, ".XXXXXX");
1567
1568 #ifdef  HAVE_MKSTEMP
1569                 if ((fd = mkstemp(thePath)) < 0) {
1570                         wsyserror(_("mkstemp (%s) failed"), thePath);
1571                         goto failure;
1572                 }
1573                 mask = umask(0);
1574                 umask(mask);
1575                 fchmod(fd, 0644 & ~mask);
1576                 if ((theFile = fdopen(fd, "wb")) == NULL) {
1577                         close(fd);
1578                 }
1579 #else
1580                 if (mktemp(thePath) == NULL) {
1581                         wsyserror(_("mktemp (%s) failed"), thePath);
1582                         goto failure;
1583                 }
1584                 theFile = fopen(thePath, "wb");
1585 #endif
1586         } else {
1587                 thePath = wstrdup(path);
1588                 theFile = fopen(thePath, "wb");
1589         }
1590
1591         if (theFile == NULL) {
1592                 wsyserror(_("open (%s) failed"), thePath);
1593                 goto failure;
1594         }
1595
1596         desc = indentedDescription(plist, 0);
1597
1598         if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1599                 wsyserror(_("writing to file: %s failed"), thePath);
1600                 wfree(desc);
1601                 goto failure;
1602         }
1603
1604         wfree(desc);
1605
1606         if (fclose(theFile) != 0) {
1607                 wsyserror(_("fclose (%s) failed"), thePath);
1608                 goto failure;
1609         }
1610
1611         /* If we used a temporary file, we still need to rename() it be the
1612          * real file.  Also, we need to try to retain the file attributes of
1613          * the original file we are overwriting (if we are) */
1614         if (atomically) {
1615                 if (rename(thePath, path) != 0) {
1616                         wsyserror(_("rename ('%s' to '%s') failed"), thePath, path);
1617                         goto failure;
1618                 }
1619         }
1620
1621         wfree(thePath);
1622         return True;
1623
1624  failure:
1625         if (atomically) {
1626                 unlink(thePath);
1627                 wfree(thePath);
1628         }
1629
1630         return False;
1631 }