- improved behaviour for the shared appicon thing.
[wmaker-crm.git] / WINGs / proplist.c
blob2e9ccad493de20e6e76d0ae4ac43f822d9f12689
3 #include <string.h>
4 #include <stdarg.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <ctype.h>
12 #include "WUtil.h"
13 #include "wconfig.h"
17 typedef enum {
18 WPLString = 0x57504c01,
19 WPLData = 0x57504c02,
20 WPLArray = 0x57504c03,
21 WPLDictionary = 0x57504c04
22 } WPLType;
25 typedef struct W_PropList {
26 WPLType type;
28 union {
29 char *string;
30 WMData *data;
31 WMArray *array;
32 WMHashTable *dict;
33 } d;
35 int retainCount;
36 } W_PropList;
39 typedef struct PLData {
40 char *ptr;
41 int pos;
42 char *filename;
43 int lineNumber;
44 } PLData;
47 typedef struct StringBuffer {
48 char *str;
49 int size;
50 } StringBuffer;
53 static unsigned hashPropList(WMPropList *plist);
54 static WMPropList* getPLString(PLData *pldata);
55 static WMPropList* getPLQString(PLData *pldata);
56 static WMPropList* getPLData(PLData *pldata);
57 static WMPropList* getPLArray(PLData *pldata);
58 static WMPropList* getPLDictionary(PLData *pldata);
59 static WMPropList* getPropList(PLData *pldata);
63 typedef unsigned (*hashFunc)(const void*);
64 typedef Bool (*isEqualFunc)(const void*, const void*);
65 typedef void* (*retainFunc)(const void*);
66 typedef void (*releaseFunc)(const void*);
68 static const WMHashTableCallbacks WMPropListHashCallbacks = {
69 (hashFunc)hashPropList,
70 (isEqualFunc)WMIsPropListEqualTo,
71 (retainFunc)NULL,
72 (releaseFunc)NULL
77 static Bool caseSensitive = True;
81 #define BUFFERSIZE 8192
82 #define BUFFERSIZE_INCREMENT 1024
85 #if 0
86 # define DPUT(s) puts(s)
87 #else
88 # define DPUT(s)
89 #endif
91 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
92 (pld)->filename ? "file" : "PropList",\
93 (pld)->filename ? (pld)->filename : "description",\
94 (pld)->lineNumber, msg)
96 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
97 || (c)=='+')
99 #define CHECK_BUFFER_SIZE(buf, ptr) \
100 if ((ptr) >= (buf).size-1) {\
101 (buf).size += BUFFERSIZE_INCREMENT;\
102 (buf).str = wrealloc((buf).str, (buf).size);\
106 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
107 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
108 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
109 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
110 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
111 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
112 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
116 #define MaxHashLength 64
118 static unsigned
119 hashPropList(WMPropList *plist)
121 unsigned ret = 0;
122 unsigned ctr = 0;
123 const char *key;
124 int i, len;
126 switch (plist->type) {
127 case WPLString:
128 key = plist->d.string;
129 len = WMIN(strlen(key), MaxHashLength);
130 for (i=0; i<len; i++) {
131 ret ^= tolower(key[i]) << ctr;
132 ctr = (ctr + 1) % sizeof (char *);
134 /*while (*key) {
135 ret ^= tolower(*key++) << ctr;
136 ctr = (ctr + 1) % sizeof (char *);
138 break;
140 case WPLData:
141 key = WMDataBytes(plist->d.data);
142 len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
143 for (i=0; i<len; i++) {
144 ret ^= key[i] << ctr;
145 ctr = (ctr + 1) % sizeof (char *);
147 break;
149 default:
150 wwarning(_("Only string or data is supported for a proplist dictionary key"));
151 wassertrv(False, 0);
152 break;
155 return ret;
158 static WMPropList*
159 retainPropListByCount(WMPropList *plist, int count)
161 WMPropList *key, *value;
162 WMHashEnumerator e;
163 int i;
165 plist->retainCount += count;
167 switch(plist->type) {
168 case WPLString:
169 case WPLData:
170 break;
171 case WPLArray:
172 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
173 retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
175 break;
176 case WPLDictionary:
177 e = WMEnumerateHashTable(plist->d.dict);
178 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
179 retainPropListByCount(key, count);
180 retainPropListByCount(value, count);
182 break;
183 default:
184 wwarning(_("Used proplist functions on non-WMPropLists objects"));
185 wassertrv(False, NULL);
186 break;
189 return plist;
193 static void
194 releasePropListByCount(WMPropList *plist, int count)
196 WMPropList *key, *value;
197 WMHashEnumerator e;
198 int i;
200 plist->retainCount -= count;
202 switch(plist->type) {
203 case WPLString:
204 if (plist->retainCount < 1) {
205 wfree(plist->d.string);
206 wfree(plist);
208 break;
209 case WPLData:
210 if (plist->retainCount < 1) {
211 WMReleaseData(plist->d.data);
212 wfree(plist);
214 break;
215 case WPLArray:
216 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
217 releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
219 if (plist->retainCount < 1) {
220 WMFreeArray(plist->d.array);
221 wfree(plist);
223 break;
224 case WPLDictionary:
225 e = WMEnumerateHashTable(plist->d.dict);
226 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
227 releasePropListByCount(key, count);
228 releasePropListByCount(value, count);
230 if (plist->retainCount < 1) {
231 WMFreeHashTable(plist->d.dict);
232 wfree(plist);
234 break;
235 default:
236 wwarning(_("Used proplist functions on non-WMPropLists objects"));
237 wassertr(False);
238 break;
243 static char*
244 dataDescription(WMPropList *plist)
246 const unsigned char *data;
247 char *retVal;
248 int i, j, length;
250 data = WMDataBytes(plist->d.data);
251 length = WMGetDataLength(plist->d.data);
253 retVal = (char*)wmalloc(2*length+length/4+3);
255 retVal[0] = '<';
256 for (i=0, j=1; i<length; i++) {
257 retVal[j++] = num2char((data[i]>>4) & 0x0f);
258 retVal[j++] = num2char(data[i] & 0x0f);
259 if ((i & 0x03)==3 && i!=length-1) {
260 /* if we've just finished a 32-bit int, add a space */
261 retVal[j++] = ' ';
264 retVal[j++] = '>';
265 retVal[j] = '\0';
267 return retVal;
271 static char*
272 stringDescription(WMPropList *plist)
274 const char *str;
275 char *retVal, *sPtr, *dPtr;
276 int len, quote;
277 unsigned char ch;
279 str = plist->d.string;
281 if (strlen(str) == 0) {
282 return wstrdup("\"\"");
285 /* FIXME: make this work with unichars. */
287 quote = 0;
288 sPtr = (char*) str;
289 len = 0;
290 while ((ch = *sPtr)) {
291 if (!noquote(ch)) {
292 quote = 1;
293 if (charesc(ch))
294 len++;
295 else if (numesc(ch))
296 len += 3;
298 sPtr++;
299 len++;
302 if (quote)
303 len += 2;
305 retVal = (char*)wmalloc(len+1);
307 sPtr = (char*) str;
308 dPtr = retVal;
310 if (quote)
311 *dPtr++ = '"';
313 while ((ch = *sPtr)) {
314 if (charesc(ch)) {
315 *(dPtr++) = '\\';
316 switch (ch) {
317 case '\a': *dPtr = 'a'; break;
318 case '\b': *dPtr = 'b'; break;
319 case '\t': *dPtr = 't'; break;
320 case '\n': *dPtr = 'n'; break;
321 case '\v': *dPtr = 'v'; break;
322 case '\f': *dPtr = 'f'; break;
323 default: *dPtr = ch; /* " or \ */
325 } else if (numesc(ch)) {
326 *(dPtr++) = '\\';
327 *(dPtr++) = '0' + ((ch>>6)&07);
328 *(dPtr++) = '0' + ((ch>>3)&07);
329 *dPtr = '0' + (ch&07);
330 } else {
331 *dPtr = ch;
333 sPtr++;
334 dPtr++;
337 if (quote)
338 *dPtr++ = '"';
340 *dPtr = '\0';
342 return retVal;
346 static char*
347 description(WMPropList *plist)
349 WMPropList *key, *val;
350 char *retstr = NULL;
351 char *str, *tmp, *skey, *sval;
352 WMHashEnumerator e;
353 int i;
355 switch (plist->type) {
356 case WPLString:
357 retstr = stringDescription(plist);
358 break;
359 case WPLData:
360 retstr = dataDescription(plist);
361 break;
362 case WPLArray:
363 retstr = wstrdup("(");
364 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
365 str = description(WMGetFromArray(plist->d.array, i));
366 if (i==0) {
367 retstr = wstrappend(retstr, str);
368 } else {
369 tmp = (char *)wmalloc(strlen(retstr)+strlen(str)+3);
370 sprintf(tmp, "%s, %s", retstr, str);
371 wfree(retstr);
372 retstr = tmp;
374 wfree(str);
376 retstr = wstrappend(retstr, ")");
377 break;
378 case WPLDictionary:
379 retstr = wstrdup("{");
380 e = WMEnumerateHashTable(plist->d.dict);
381 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&val, (void**)&key)) {
382 skey = description(key);
383 sval = description(val);
384 tmp = (char*)wmalloc(strlen(retstr)+strlen(skey)+strlen(sval)+5);
385 sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
386 wfree(skey);
387 wfree(sval);
388 wfree(retstr);
389 retstr = tmp;
391 retstr = wstrappend(retstr, "}");
392 break;
393 default:
394 wwarning(_("Used proplist functions on non-WMPropLists objects"));
395 wassertrv(False, NULL);
396 break;
399 return retstr;
403 static char*
404 indentedDescription(WMPropList *plist, int level)
406 WMPropList *key, *val;
407 char *retstr = NULL;
408 char *str, *tmp, *skey, *sval;
409 WMHashEnumerator e;
410 int i;
412 if (plist->type==WPLArray/* || plist->type==WPLDictionary*/) {
413 retstr = description(plist);
415 if (retstr && ((2*(level+1) + strlen(retstr)) <= 77)) {
416 return retstr;
417 } else if (retstr) {
418 wfree(retstr);
419 retstr = NULL;
423 switch (plist->type) {
424 case WPLString:
425 retstr = stringDescription(plist);
426 break;
427 case WPLData:
428 retstr = dataDescription(plist);
429 break;
430 case WPLArray:
431 retstr = wstrdup("(\n");
432 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
433 str = indentedDescription(WMGetFromArray(plist->d.array, i),
434 level+1);
435 if (i==0) {
436 tmp = (char*)wmalloc(2*(level+1)+strlen(retstr)+strlen(str)+1);
437 sprintf(tmp, "%s%*s%s", retstr, 2*(level+1), "", str);
438 wfree(retstr);
439 retstr = tmp;
440 } else {
441 tmp = (char*)wmalloc(2*(level+1)+strlen(retstr)+strlen(str)+3);
442 sprintf(tmp, "%s,\n%*s%s", retstr, 2*(level+1), "", str);
443 wfree(retstr);
444 retstr = tmp;
446 wfree(str);
448 tmp = (char*)wmalloc(strlen(retstr) + 2*level + 3);
449 sprintf(tmp, "%s\n%*s)", retstr, 2*level, "");
450 wfree(retstr);
451 retstr = tmp;
452 break;
453 case WPLDictionary:
454 retstr = wstrdup("{\n");
455 e = WMEnumerateHashTable(plist->d.dict);
456 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&val, (void**)&key)) {
457 skey = indentedDescription(key, level+1);
458 sval = indentedDescription(val, level+1);
459 tmp = (char*)wmalloc(2*(level+1) + strlen(retstr) + strlen(skey)
460 + strlen(sval) + 6);
461 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2*(level+1), "",
462 skey, sval);
463 wfree(skey);
464 wfree(sval);
465 wfree(retstr);
466 retstr = tmp;
468 tmp = (char*)wmalloc(strlen(retstr) + 2*level + 2);
469 sprintf(tmp, "%s%*s}", retstr, 2*level, "");
470 wfree(retstr);
471 retstr = tmp;
472 break;
473 default:
474 wwarning(_("Used proplist functions on non-WMPropLists objects"));
475 wassertrv(False, NULL);
476 break;
479 return retstr;
483 static INLINE int
484 getChar(PLData *pldata)
486 int c;
488 c = pldata->ptr[pldata->pos];
489 if (c==0) {
490 return 0;
493 pldata->pos++;
495 if (c == '\n')
496 pldata->lineNumber++;
498 return c;
502 static INLINE int
503 getNonSpaceChar(PLData *pldata)
505 int c;
507 while (1) {
508 c = pldata->ptr[pldata->pos];
509 if (c==0) {
510 break;
512 pldata->pos++;
513 if (c == '\n') {
514 pldata->lineNumber++;
515 } else if (!isspace(c)) {
516 break;
520 return c;
524 static char*
525 unescapestr(char *src)
527 char *dest = wmalloc(strlen(src)+1);
528 char *sPtr, *dPtr;
529 char ch;
532 for (sPtr=src, dPtr=dest; *sPtr; sPtr++, dPtr++) {
533 if(*sPtr != '\\') {
534 *dPtr = *sPtr;
535 } else {
536 ch = *(++sPtr);
537 if((ch>='0') && (ch<='3')) {
538 /* assume next 2 chars are octal too */
539 *dPtr = ((ch & 07) << 6);
540 *dPtr |= ((*(++sPtr)&07)<<3);
541 *dPtr |= *(++sPtr)&07;
542 } else {
543 switch(ch) {
544 case 'a' : *dPtr = '\a'; break;
545 case 'b' : *dPtr = '\b'; break;
546 case 't' : *dPtr = '\t'; break;
547 case 'r' : *dPtr = '\r'; break;
548 case 'n' : *dPtr = '\n'; break;
549 case 'v' : *dPtr = '\v'; break;
550 case 'f' : *dPtr = '\f'; break;
551 default : *dPtr = *sPtr;
557 *dPtr = 0;
559 return dest;
563 static WMPropList*
564 getPLString(PLData *pldata)
566 WMPropList *plist;
567 StringBuffer sBuf;
568 int ptr = 0;
569 int c;
571 sBuf.str = wmalloc(BUFFERSIZE);
572 sBuf.size = BUFFERSIZE;
574 while (1) {
575 c = getChar(pldata);
576 if (ISSTRINGABLE(c)) {
577 CHECK_BUFFER_SIZE(sBuf, ptr);
578 sBuf.str[ptr++] = c;
579 } else {
580 if (c != 0) {
581 pldata->pos--;
583 break;
587 sBuf.str[ptr] = 0;
589 if (ptr == 0) {
590 plist = NULL;
591 } else {
592 char *tmp = unescapestr(sBuf.str);
593 plist = WMCreatePLString(tmp);
594 wfree(tmp);
597 wfree(sBuf.str);
599 return plist;
603 static WMPropList*
604 getPLQString(PLData *pldata)
606 WMPropList *plist;
607 int ptr = 0, escaping = 0, ok = 1;
608 int c;
609 StringBuffer sBuf;
611 sBuf.str = wmalloc(BUFFERSIZE);
612 sBuf.size = BUFFERSIZE;
614 while (1) {
615 c = getChar(pldata);
616 if (!escaping) {
617 if (c == '\\') {
618 escaping = 1;
619 continue;
620 } else if (c == '"') {
621 break;
623 } else {
624 CHECK_BUFFER_SIZE(sBuf, ptr);
625 sBuf.str[ptr++] = '\\';
626 escaping = 0;
629 if (c == 0) {
630 COMPLAIN(pldata, _("unterminated PropList string"));
631 ok = 0;
632 break;
633 } else {
634 CHECK_BUFFER_SIZE(sBuf, ptr);
635 sBuf.str[ptr++] = c;
639 sBuf.str[ptr] = 0;
641 if (!ok) {
642 plist = NULL;
643 } else {
644 char *tmp = unescapestr(sBuf.str);
645 plist = WMCreatePLString(tmp);
646 wfree(tmp);
649 wfree(sBuf.str);
651 return plist;
655 static WMPropList*
656 getPLData(PLData *pldata)
658 int ok = 1;
659 int len = 0;
660 int c1, c2;
661 unsigned char buf[BUFFERSIZE], byte;
662 WMPropList *plist;
663 WMData *data;
665 data = WMCreateDataWithCapacity(0);
667 while (1) {
668 c1 = getNonSpaceChar(pldata);
669 if (c1 == 0) {
670 COMPLAIN(pldata, _("unterminated PropList data"));
671 ok = 0;
672 break;
673 } else if (c1=='>') {
674 break;
675 } else if (ishexdigit(c1)) {
676 c2 = getNonSpaceChar(pldata);
677 if (c2==0 || c2=='>') {
678 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
679 ok = 0;
680 break;
681 } else if (ishexdigit(c2)) {
682 byte = char2num(c1) << 4;
683 byte |= char2num(c2);
684 buf[len++] = byte;
685 if (len == sizeof(buf)) {
686 WMAppendDataBytes(data, buf, len);
687 len = 0;
689 } else {
690 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
691 ok = 0;
692 break;
697 if (!ok) {
698 WMReleaseData(data);
699 return NULL;
702 if (len > 0)
703 WMAppendDataBytes(data, buf, len);
705 plist = WMCreatePLData(data);
706 WMReleaseData(data);
708 return plist;
712 static WMPropList*
713 getPLArray(PLData *pldata)
715 Bool first = True;
716 int ok=1;
717 int c;
718 WMPropList *array, *obj;
720 array = WMCreatePLArray(NULL);
722 while (1) {
723 c = getNonSpaceChar(pldata);
724 if (c == 0) {
725 COMPLAIN(pldata, _("unterminated PropList array"));
726 ok = 0;
727 break;
728 } else if (c == ')') {
729 break;
730 } else if (c == ',') {
731 /* continue normally */
732 } else if (!first) {
733 COMPLAIN(pldata, _("missing or unterminated PropList array"));
734 ok = 0;
735 break;
736 } else {
737 pldata->pos--;
739 first = False;
741 obj = getPropList(pldata);
742 if (!obj) {
743 COMPLAIN(pldata, _("could not get PropList array element"));
744 ok = 0;
745 break;
747 WMAddToPLArray(array, obj);
748 WMReleasePropList(obj);
751 if (!ok) {
752 WMReleasePropList(array);
753 array = NULL;
756 return array;
760 static WMPropList*
761 getPLDictionary(PLData *pldata)
763 int ok = 1;
764 int c;
765 WMPropList *dict, *key, *value;
767 dict = WMCreatePLDictionary(NULL, NULL);
769 while (1) {
770 c = getNonSpaceChar(pldata);
771 if (c==0) {
772 COMPLAIN(pldata, _("unterminated PropList dictionary"));
773 ok = 0;
774 break;
775 } else if (c=='}') {
776 break;
779 DPUT("getting PropList dictionary key");
780 if (c == '<') {
781 key = getPLData(pldata);
782 } else if (c == '"') {
783 key = getPLQString(pldata);
784 } else if (ISSTRINGABLE(c)) {
785 pldata->pos--;
786 key = getPLString(pldata);
787 } else {
788 if (c == '=') {
789 COMPLAIN(pldata, _("missing PropList dictionary key"));
790 } else {
791 COMPLAIN(pldata, _("missing PropList dictionary entry key "
792 "or unterminated dictionary"));
794 ok = 0;
795 break;
798 if (!key) {
799 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
800 ok = 0;
801 break;
804 c = getNonSpaceChar(pldata);
805 if (c != '=') {
806 WMReleasePropList(key);
807 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
808 ok = 0;
809 break;
812 DPUT("getting PropList dictionary entry value for key");
813 value = getPropList(pldata);
814 if (!value) {
815 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
816 WMReleasePropList(key);
817 ok = 0;
818 break;
821 c = getNonSpaceChar(pldata);
822 if (c != ';') {
823 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
824 WMReleasePropList(key);
825 WMReleasePropList(value);
826 ok = 0;
827 break;
830 WMPutInPLDictionary(dict, key, value);
831 WMReleasePropList(key);
832 WMReleasePropList(value);
835 if (!ok) {
836 WMReleasePropList(dict);
837 dict = NULL;
840 return dict;
844 static WMPropList*
845 getPropList(PLData *pldata)
847 WMPropList *plist;
848 int c;
850 c = getNonSpaceChar(pldata);
852 switch(c) {
853 case 0:
854 DPUT("End of PropList");
855 plist = NULL;
856 break;
858 case '{':
859 DPUT("Getting PropList dictionary");
860 plist = getPLDictionary(pldata);
861 break;
863 case '(':
864 DPUT("Getting PropList srrsy");
865 plist = getPLArray(pldata);
866 break;
868 case '<':
869 DPUT("Getting PropList data");
870 plist = getPLData(pldata);
871 break;
873 case '"':
874 DPUT("Getting PropList quoted string");
875 plist = getPLQString(pldata);
876 break;
878 default:
879 if (ISSTRINGABLE(c)) {
880 DPUT("Getting PropList string");
881 pldata->pos--;
882 plist = getPLString(pldata);
883 } else {
884 COMPLAIN(pldata, _("was expecting a string, data, array or "
885 "dictionary. If it's a string, try enclosing "
886 "it with \"."));
887 if (c=='#' || c=='/') {
888 wwarning(_("Comments are not allowed inside WindowMaker owned"
889 " domain files."));
891 plist = NULL;
893 break;
896 return plist;
900 void
901 WMPLSetCaseSensitive(Bool caseSensitiveness)
903 caseSensitive = caseSensitiveness;
907 WMPropList*
908 WMCreatePLString(char *str)
910 WMPropList *plist;
912 wassertrv(str!=NULL, NULL);
914 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
916 plist->type = WPLString;
917 plist->d.string = wstrdup(str);
918 plist->retainCount = 1;
920 return plist;
924 WMPropList*
925 WMCreatePLData(WMData *data)
927 WMPropList *plist;
929 wassertrv(data!=NULL, NULL);
931 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
933 plist->type = WPLData;
934 plist->d.data = WMRetainData(data);
935 plist->retainCount = 1;
937 return plist;
941 WMPropList*
942 WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
944 WMPropList *plist;
946 wassertrv(bytes!=NULL, NULL);
948 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
950 plist->type = WPLData;
951 plist->d.data = WMCreateDataWithBytes(bytes, length);
952 plist->retainCount = 1;
954 return plist;
958 WMPropList*
959 WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length,
960 WMFreeDataProc *destructor)
962 WMPropList *plist;
964 wassertrv(bytes!=NULL, NULL);
966 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
968 plist->type = WPLData;
969 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
970 plist->retainCount = 1;
972 return plist;
976 WMPropList*
977 WMCreatePLArray(WMPropList *elem, ...)
979 WMPropList *plist, *nelem;
980 va_list ap;
982 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
983 plist->type = WPLArray;
984 plist->d.array = WMCreateArray(4);
985 plist->retainCount = 1;
987 if (!elem)
988 return plist;
990 WMAddToArray(plist->d.array, WMRetainPropList(elem));
992 va_start(ap, elem);
994 while (1) {
995 nelem = va_arg(ap, WMPropList*);
996 if(!nelem) {
997 va_end(ap);
998 return plist;
1000 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
1005 WMPropList*
1006 WMCreatePLDictionary(WMPropList *key, WMPropList *value, ...)
1008 WMPropList *plist, *nkey, *nvalue, *k, *v;
1009 va_list ap;
1011 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
1012 plist->type = WPLDictionary;
1013 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
1014 plist->retainCount = 1;
1016 if (!key || !value)
1017 return plist;
1019 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
1021 va_start(ap, value);
1023 while (1) {
1024 nkey = va_arg(ap, WMPropList*);
1025 if (!nkey) {
1026 va_end(ap);
1027 return plist;
1029 nvalue = va_arg(ap, WMPropList*);
1030 if (!nvalue) {
1031 va_end(ap);
1032 return plist;
1034 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void**)&v, (void**)&k)) {
1035 WMHashRemove(plist->d.dict, k);
1036 WMReleasePropList(k);
1037 WMReleasePropList(v);
1039 WMHashInsert(plist->d.dict, WMRetainPropList(nkey),
1040 WMRetainPropList(nvalue));
1045 WMPropList*
1046 WMRetainPropList(WMPropList *plist)
1048 WMPropList *key, *value;
1049 WMHashEnumerator e;
1050 int i;
1052 plist->retainCount++;
1054 switch(plist->type) {
1055 case WPLString:
1056 case WPLData:
1057 break;
1058 case WPLArray:
1059 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1060 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1062 break;
1063 case WPLDictionary:
1064 e = WMEnumerateHashTable(plist->d.dict);
1065 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1066 WMRetainPropList(key);
1067 WMRetainPropList(value);
1069 break;
1070 default:
1071 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1072 wassertrv(False, NULL);
1073 break;
1076 return plist;
1080 void
1081 WMReleasePropList(WMPropList *plist)
1083 WMPropList *key, *value;
1084 WMHashEnumerator e;
1085 int i;
1087 plist->retainCount--;
1089 switch(plist->type) {
1090 case WPLString:
1091 if (plist->retainCount < 1) {
1092 wfree(plist->d.string);
1093 wfree(plist);
1095 break;
1096 case WPLData:
1097 if (plist->retainCount < 1) {
1098 WMReleaseData(plist->d.data);
1099 wfree(plist);
1101 break;
1102 case WPLArray:
1103 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1104 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1106 if (plist->retainCount < 1) {
1107 WMFreeArray(plist->d.array);
1108 wfree(plist);
1110 break;
1111 case WPLDictionary:
1112 e = WMEnumerateHashTable(plist->d.dict);
1113 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1114 WMReleasePropList(key);
1115 WMReleasePropList(value);
1117 if (plist->retainCount < 1) {
1118 WMFreeHashTable(plist->d.dict);
1119 wfree(plist);
1121 break;
1122 default:
1123 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1124 wassertr(False);
1125 break;
1130 void
1131 WMInsertInPLArray(WMPropList *plist, int index, WMPropList *item)
1133 wassertr(plist->type==WPLArray);
1135 retainPropListByCount(item, plist->retainCount);
1136 WMInsertInArray(plist->d.array, index, item);
1140 void
1141 WMAddToPLArray(WMPropList *plist, WMPropList *item)
1143 wassertr(plist->type==WPLArray);
1145 retainPropListByCount(item, plist->retainCount);
1146 WMAddToArray(plist->d.array, item);
1150 void
1151 WMDeleteFromPLArray(WMPropList *plist, int index)
1153 WMPropList *item;
1155 wassertr(plist->type==WPLArray);
1157 item = WMGetFromArray(plist->d.array, index);
1158 if (item != NULL) {
1159 WMDeleteFromArray(plist->d.array, index);
1160 releasePropListByCount(item, plist->retainCount);
1165 void
1166 WMRemoveFromPLArray(WMPropList *plist, WMPropList *item)
1168 WMPropList *iPtr;
1169 int i;
1171 wassertr(plist->type==WPLArray);
1173 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1174 iPtr = WMGetFromArray(plist->d.array, i);
1175 if (WMIsPropListEqualTo(item, iPtr)) {
1176 WMDeleteFromArray(plist->d.array, i);
1177 releasePropListByCount(iPtr, plist->retainCount);
1178 break;
1184 void
1185 WMPutInPLDictionary(WMPropList *plist, WMPropList *key, WMPropList *value)
1187 wassertr(plist->type==WPLDictionary);
1189 /*WMRetainPropList(key);*/
1190 WMRemoveFromPLDictionary(plist, key);
1191 retainPropListByCount(key, plist->retainCount);
1192 retainPropListByCount(value, plist->retainCount);
1193 WMHashInsert(plist->d.dict, key, value);
1194 /*WMReleasePropList(key);*/
1198 void
1199 WMRemoveFromPLDictionary(WMPropList *plist, WMPropList *key)
1201 WMPropList *k, *v;
1203 wassertr(plist->type==WPLDictionary);
1205 if (WMHashGetItemAndKey(plist->d.dict, key, (void**)&v, (void**)&k)) {
1206 WMHashRemove(plist->d.dict, k);
1207 releasePropListByCount(k, plist->retainCount);
1208 releasePropListByCount(v, plist->retainCount);
1213 WMPropList*
1214 WMMergePLDictionaries(WMPropList *dest, WMPropList *source, Bool recursive)
1216 WMPropList *key, *value, *dvalue;
1217 WMHashEnumerator e;
1219 wassertr(source->type==WPLDictionary && dest->type==WPLDictionary);
1221 e = WMEnumerateHashTable(source->d.dict);
1222 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1223 if (recursive && value->type==WPLDictionary) {
1224 dvalue = WMGetFromPLDictionary(dest, key);
1225 if (dvalue && dvalue->type==WPLDictionary) {
1226 WMMergePLDictionaries(dvalue, value, recursive);
1227 } else {
1228 WMPutInPLDictionary(dest, key, value);
1230 } else {
1231 WMPutInPLDictionary(dest, key, value);
1235 return dest;
1240 WMGetPropListItemCount(WMPropList *plist)
1242 switch(plist->type) {
1243 case WPLString:
1244 case WPLData:
1245 return 0; /* should this be 1 instead? */
1246 case WPLArray:
1247 return WMGetArrayItemCount(plist->d.array);
1248 case WPLDictionary:
1249 return (int)WMCountHashTable(plist->d.dict);
1250 default:
1251 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1252 wassertrv(False, 0);
1253 break;
1256 return 0;
1260 Bool
1261 WMIsPLString(WMPropList *plist)
1263 return (plist->type == WPLString);
1267 Bool
1268 WMIsPLData(WMPropList *plist)
1270 return (plist->type == WPLData);
1274 Bool
1275 WMIsPLArray(WMPropList *plist)
1277 return (plist->type == WPLArray);
1281 Bool
1282 WMIsPLDictionary(WMPropList *plist)
1284 return (plist->type == WPLDictionary);
1288 Bool
1289 WMIsPropListEqualTo(WMPropList *plist, WMPropList *other)
1291 WMPropList *key1, *item1, *item2;
1292 WMHashEnumerator enumerator;
1293 int n, i;
1295 if (plist->type != other->type)
1296 return False;
1298 switch(plist->type) {
1299 case WPLString:
1300 if (caseSensitive) {
1301 return (strcmp(plist->d.string, other->d.string) == 0);
1302 } else {
1303 return (strcasecmp(plist->d.string, other->d.string) == 0);
1305 case WPLData:
1306 return WMIsDataEqualToData(plist->d.data, other->d.data);
1307 case WPLArray:
1308 n = WMGetArrayItemCount(plist->d.array);
1309 if (n != WMGetArrayItemCount(other->d.array))
1310 return False;
1311 for (i=0; i<n; i++) {
1312 item1 = WMGetFromArray(plist->d.array, i);
1313 item2 = WMGetFromArray(other->d.array, i);
1314 if (!WMIsPropListEqualTo(item1, item2))
1315 return False;
1317 return True;
1318 case WPLDictionary:
1319 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1320 return False;
1321 enumerator = WMEnumerateHashTable(plist->d.dict);
1322 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void**)&item1,
1323 (void**)&key1)) {
1324 item2 = WMHashGet(other->d.dict, key1);
1325 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1326 return False;
1328 return True;
1329 default:
1330 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1331 wassertrv(False, False);
1332 break;
1335 return False;
1339 char*
1340 WMGetFromPLString(WMPropList *plist)
1342 wassertrv(plist->type==WPLString, NULL);
1344 return plist->d.string;
1348 WMData*
1349 WMGetFromPLData(WMPropList *plist)
1351 wassertrv(plist->type==WPLData, NULL);
1353 return plist->d.data;
1357 const unsigned char*
1358 WMGetPLDataBytes(WMPropList *plist)
1360 wassertrv(plist->type==WPLData, NULL);
1362 return WMDataBytes(plist->d.data);
1367 WMGetPLDataLength(WMPropList *plist)
1369 wassertrv(plist->type==WPLData, 0);
1371 return WMGetDataLength(plist->d.data);
1375 WMPropList*
1376 WMGetFromPLArray(WMPropList *plist, int index)
1378 wassertrv(plist->type==WPLArray, NULL);
1380 return WMGetFromArray(plist->d.array, index);
1384 WMPropList*
1385 WMGetFromPLDictionary(WMPropList *plist, WMPropList *key)
1387 wassertrv(plist->type==WPLDictionary, NULL);
1389 return WMHashGet(plist->d.dict, key);
1393 WMPropList*
1394 WMGetPLDictionaryKeys(WMPropList *plist)
1396 WMPropList *array, *key;
1397 WMHashEnumerator enumerator;
1399 wassertrv(plist->type==WPLDictionary, NULL);
1401 array = (WMPropList*)wmalloc(sizeof(W_PropList));
1402 array->type = WPLArray;
1403 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1404 array->retainCount = 1;
1406 enumerator = WMEnumerateHashTable(plist->d.dict);
1407 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1408 WMAddToArray(array->d.array, WMRetainPropList(key));
1411 return array;
1415 WMPropList*
1416 WMShallowCopyPropList(WMPropList *plist)
1418 WMPropList *ret = NULL;
1419 WMPropList *key, *item;
1420 WMHashEnumerator e;
1421 WMData *data;
1422 int i;
1424 switch(plist->type) {
1425 case WPLString:
1426 ret = WMCreatePLString(plist->d.string);
1427 break;
1428 case WPLData:
1429 data = WMCreateDataWithData(plist->d.data);
1430 ret = WMCreatePLData(data);
1431 WMReleaseData(data);
1432 break;
1433 case WPLArray:
1434 ret = (WMPropList*)wmalloc(sizeof(W_PropList));
1435 ret->type = WPLArray;
1436 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1437 ret->retainCount = 1;
1439 for(i=0; i<WMGetArrayItemCount(ret->d.array); i++)
1440 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1442 break;
1443 case WPLDictionary:
1444 ret = WMCreatePLDictionary(NULL, NULL);
1445 e = WMEnumerateHashTable(plist->d.dict);
1446 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&item, (void**)&key)) {
1447 WMPutInPLDictionary(ret, key, item);
1449 break;
1450 default:
1451 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1452 wassertrv(False, NULL);
1453 break;
1456 return ret;
1460 WMPropList*
1461 WMDeepCopyPropList(WMPropList *plist)
1463 WMPropList *ret = NULL;
1464 WMPropList *key, *item;
1465 WMHashEnumerator e;
1466 WMData *data;
1467 int i;
1469 switch(plist->type) {
1470 case WPLString:
1471 ret = WMCreatePLString(plist->d.string);
1472 break;
1473 case WPLData:
1474 data = WMCreateDataWithData(plist->d.data);
1475 ret = WMCreatePLData(data);
1476 WMReleaseData(data);
1477 break;
1478 case WPLArray:
1479 ret = WMCreatePLArray(NULL);
1480 for(i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1481 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1482 WMAddToArray(ret->d.array, item);
1484 break;
1485 case WPLDictionary:
1486 ret = WMCreatePLDictionary(NULL, NULL);
1487 e = WMEnumerateHashTable(plist->d.dict);
1488 /* While we copy an existing dictionary there is no way that we can
1489 * have duplicate keys, so we don't need to first remove a key/value
1490 * pair before inserting the new key/value.
1492 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&item, (void**)&key)) {
1493 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key),
1494 WMDeepCopyPropList(item));
1496 break;
1497 default:
1498 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1499 wassertrv(False, NULL);
1500 break;
1503 return ret;
1507 WMPropList*
1508 WMCreatePropListFromDescription(char *desc)
1510 WMPropList *plist = NULL;
1511 PLData *pldata;
1513 pldata = (PLData*) wmalloc(sizeof(PLData));
1514 memset(pldata, 0, sizeof(PLData));
1515 pldata->ptr = desc;
1516 pldata->lineNumber = 1;
1518 plist = getPropList(pldata);
1520 if (getNonSpaceChar(pldata)!=0 && plist) {
1521 COMPLAIN(pldata, _("extra data after end of property list"));
1523 * We can't just ignore garbage after the end of the description
1524 * (especially if the description was read from a file), because
1525 * the "garbage" can be the real data and the real garbage is in
1526 * fact in the beginning of the file (which is now inside plist)
1528 WMReleasePropList(plist);
1529 plist = NULL;
1532 wfree(pldata);
1534 return plist;
1538 char*
1539 WMGetPropListDescription(WMPropList *plist, Bool indented)
1541 return (indented ? indentedDescription(plist, 0) : description(plist));
1545 WMPropList*
1546 WMReadPropListFromFile(char *file)
1548 WMPropList *plist = NULL;
1549 PLData *pldata;
1550 FILE *f;
1551 struct stat stbuf;
1552 size_t length;
1554 f = fopen(file, "r");
1555 if (!f) {
1556 /* let the user print the error message if he really needs to */
1557 /*wsyserror(_("could not open domain file '%s' for reading"), file);*/
1558 return NULL;
1561 if (stat(file, &stbuf)==0) {
1562 length = (size_t) stbuf.st_size;
1563 } else {
1564 wsyserror(_("could not get size for file '%s'"), file);
1565 fclose(f);
1566 return NULL;
1569 pldata = (PLData*) wmalloc(sizeof(PLData));
1570 memset(pldata, 0, sizeof(PLData));
1571 pldata->ptr = (char*) wmalloc(length+1);
1572 pldata->filename = file;
1573 pldata->lineNumber = 1;
1575 if (fread(pldata->ptr, length, 1, f) != 1) {
1576 wsyserror(_("error reading from file '%s'"), file);
1577 plist = NULL;
1578 goto cleanup;
1581 pldata->ptr[length] = 0;
1583 plist = getPropList(pldata);
1585 if (getNonSpaceChar(pldata)!=0 && plist) {
1586 COMPLAIN(pldata, _("extra data after end of property list"));
1588 * We can't just ignore garbage after the end of the description
1589 * (especially if the description was read from a file), because
1590 * the "garbage" can be the real data and the real garbage is in
1591 * fact in the beginning of the file (which is now inside plist)
1593 WMReleasePropList(plist);
1594 plist = NULL;
1597 cleanup:
1598 wfree(pldata->ptr);
1599 wfree(pldata);
1600 fclose(f);
1602 return plist;
1606 /* TODO: review this function's code */
1608 Bool
1609 WMWritePropListToFile(WMPropList *plist, char *path, Bool atomically)
1611 char *thePath=NULL;
1612 char *desc;
1613 FILE *theFile;
1615 if (atomically) {
1616 #ifdef HAVE_MKSTEMP
1617 int fd, mask;
1618 #endif
1620 /* Use the path name of the destination file as a prefix for the
1621 * mkstemp() call so that we can be sure that both files are on
1622 * the same filesystem and the subsequent rename() will work. */
1623 thePath = wstrconcat(path, ".XXXXXX");
1625 #ifdef HAVE_MKSTEMP
1626 if ((fd = mkstemp(thePath)) < 0) {
1627 wsyserror(_("mkstemp (%s) failed"), thePath);
1628 goto failure;
1630 mask = umask(0);
1631 umask(mask);
1632 fchmod(fd, 0644 & ~mask);
1633 if ((theFile = fdopen(fd, "w")) == NULL) {
1634 close(fd);
1636 #else
1637 if (mktemp(thePath) == NULL) {
1638 wsyserror(_("mktemp (%s) failed"), thePath);
1639 goto failure;
1641 theFile = fopen(thePath, "wb");
1642 #endif
1643 } else {
1644 thePath = wstrdup(path);
1645 theFile = fopen(thePath, "wb");
1648 if (theFile == NULL) {
1649 wsyserror(_("open (%s) failed"), thePath);
1650 goto failure;
1653 desc = indentedDescription(plist, 0);
1655 if (fprintf(theFile, "%s\n", desc) != strlen(desc)+1) {
1656 wsyserror(_("writing to file: %s failed"), thePath);
1657 wfree(desc);
1658 goto failure;
1661 wfree(desc);
1663 if (fclose(theFile) != 0) {
1664 wsyserror(_("fclose (%s) failed"), thePath);
1665 goto failure;
1668 /* If we used a temporary file, we still need to rename() it be the
1669 * real file. Also, we need to try to retain the file attributes of
1670 * the original file we are overwriting (if we are) */
1671 if (atomically) {
1672 if (rename(thePath, path) != 0) {
1673 wsyserror(_("rename ('%s' to '%s') failed"), thePath, path);
1674 goto failure;
1678 wfree(thePath);
1679 return True;
1681 failure:
1682 if (atomically) {
1683 unlink(thePath);
1684 wfree(thePath);
1687 return False;