changed indentation to use spaces only
[wmaker-crm.git] / WINGs / proplist.c
blob51461205776681d09dd5cc74ece4d9a11bd3be4c
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;
694 } else {
695 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
696 ok = 0;
697 break;
701 if (!ok) {
702 WMReleaseData(data);
703 return NULL;
706 if (len > 0)
707 WMAppendDataBytes(data, buf, len);
709 plist = WMCreatePLData(data);
710 WMReleaseData(data);
712 return plist;
716 static WMPropList*
717 getPLArray(PLData *pldata)
719 Bool first = True;
720 int ok=1;
721 int c;
722 WMPropList *array, *obj;
724 array = WMCreatePLArray(NULL);
726 while (1) {
727 c = getNonSpaceChar(pldata);
728 if (c == 0) {
729 COMPLAIN(pldata, _("unterminated PropList array"));
730 ok = 0;
731 break;
732 } else if (c == ')') {
733 break;
734 } else if (c == ',') {
735 /* continue normally */
736 } else if (!first) {
737 COMPLAIN(pldata, _("missing or unterminated PropList array"));
738 ok = 0;
739 break;
740 } else {
741 pldata->pos--;
743 first = False;
745 obj = getPropList(pldata);
746 if (!obj) {
747 COMPLAIN(pldata, _("could not get PropList array element"));
748 ok = 0;
749 break;
751 WMAddToPLArray(array, obj);
752 WMReleasePropList(obj);
755 if (!ok) {
756 WMReleasePropList(array);
757 array = NULL;
760 return array;
764 static WMPropList*
765 getPLDictionary(PLData *pldata)
767 int ok = 1;
768 int c;
769 WMPropList *dict, *key, *value;
771 dict = WMCreatePLDictionary(NULL, NULL);
773 while (1) {
774 c = getNonSpaceChar(pldata);
775 if (c==0) {
776 COMPLAIN(pldata, _("unterminated PropList dictionary"));
777 ok = 0;
778 break;
779 } else if (c=='}') {
780 break;
783 DPUT("getting PropList dictionary key");
784 if (c == '<') {
785 key = getPLData(pldata);
786 } else if (c == '"') {
787 key = getPLQString(pldata);
788 } else if (ISSTRINGABLE(c)) {
789 pldata->pos--;
790 key = getPLString(pldata);
791 } else {
792 if (c == '=') {
793 COMPLAIN(pldata, _("missing PropList dictionary key"));
794 } else {
795 COMPLAIN(pldata, _("missing PropList dictionary entry key "
796 "or unterminated dictionary"));
798 ok = 0;
799 break;
802 if (!key) {
803 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
804 ok = 0;
805 break;
808 c = getNonSpaceChar(pldata);
809 if (c != '=') {
810 WMReleasePropList(key);
811 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
812 ok = 0;
813 break;
816 DPUT("getting PropList dictionary entry value for key");
817 value = getPropList(pldata);
818 if (!value) {
819 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
820 WMReleasePropList(key);
821 ok = 0;
822 break;
825 c = getNonSpaceChar(pldata);
826 if (c != ';') {
827 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
828 WMReleasePropList(key);
829 WMReleasePropList(value);
830 ok = 0;
831 break;
834 WMPutInPLDictionary(dict, key, value);
835 WMReleasePropList(key);
836 WMReleasePropList(value);
839 if (!ok) {
840 WMReleasePropList(dict);
841 dict = NULL;
844 return dict;
848 static WMPropList*
849 getPropList(PLData *pldata)
851 WMPropList *plist;
852 int c;
854 c = getNonSpaceChar(pldata);
856 switch(c) {
857 case 0:
858 DPUT("End of PropList");
859 plist = NULL;
860 break;
862 case '{':
863 DPUT("Getting PropList dictionary");
864 plist = getPLDictionary(pldata);
865 break;
867 case '(':
868 DPUT("Getting PropList array");
869 plist = getPLArray(pldata);
870 break;
872 case '<':
873 DPUT("Getting PropList data");
874 plist = getPLData(pldata);
875 break;
877 case '"':
878 DPUT("Getting PropList quoted string");
879 plist = getPLQString(pldata);
880 break;
882 default:
883 if (ISSTRINGABLE(c)) {
884 DPUT("Getting PropList string");
885 pldata->pos--;
886 plist = getPLString(pldata);
887 } else {
888 COMPLAIN(pldata, _("was expecting a string, data, array or "
889 "dictionary. If it's a string, try enclosing "
890 "it with \"."));
891 if (c=='#' || c=='/') {
892 wwarning(_("Comments are not allowed inside WindowMaker owned"
893 " domain files."));
895 plist = NULL;
897 break;
900 return plist;
904 void
905 WMPLSetCaseSensitive(Bool caseSensitiveness)
907 caseSensitive = caseSensitiveness;
911 WMPropList*
912 WMCreatePLString(char *str)
914 WMPropList *plist;
916 wassertrv(str!=NULL, NULL);
918 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
920 plist->type = WPLString;
921 plist->d.string = wstrdup(str);
922 plist->retainCount = 1;
924 return plist;
928 WMPropList*
929 WMCreatePLData(WMData *data)
931 WMPropList *plist;
933 wassertrv(data!=NULL, NULL);
935 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
937 plist->type = WPLData;
938 plist->d.data = WMRetainData(data);
939 plist->retainCount = 1;
941 return plist;
945 WMPropList*
946 WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
948 WMPropList *plist;
950 wassertrv(bytes!=NULL, NULL);
952 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
954 plist->type = WPLData;
955 plist->d.data = WMCreateDataWithBytes(bytes, length);
956 plist->retainCount = 1;
958 return plist;
962 WMPropList*
963 WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length,
964 WMFreeDataProc *destructor)
966 WMPropList *plist;
968 wassertrv(bytes!=NULL, NULL);
970 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
972 plist->type = WPLData;
973 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
974 plist->retainCount = 1;
976 return plist;
980 WMPropList*
981 WMCreatePLArray(WMPropList *elem, ...)
983 WMPropList *plist, *nelem;
984 va_list ap;
986 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
987 plist->type = WPLArray;
988 plist->d.array = WMCreateArray(4);
989 plist->retainCount = 1;
991 if (!elem)
992 return plist;
994 WMAddToArray(plist->d.array, WMRetainPropList(elem));
996 va_start(ap, elem);
998 while (1) {
999 nelem = va_arg(ap, WMPropList*);
1000 if(!nelem) {
1001 va_end(ap);
1002 return plist;
1004 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
1009 WMPropList*
1010 WMCreatePLDictionary(WMPropList *key, WMPropList *value, ...)
1012 WMPropList *plist, *nkey, *nvalue, *k, *v;
1013 va_list ap;
1015 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
1016 plist->type = WPLDictionary;
1017 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
1018 plist->retainCount = 1;
1020 if (!key || !value)
1021 return plist;
1023 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
1025 va_start(ap, value);
1027 while (1) {
1028 nkey = va_arg(ap, WMPropList*);
1029 if (!nkey) {
1030 va_end(ap);
1031 return plist;
1033 nvalue = va_arg(ap, WMPropList*);
1034 if (!nvalue) {
1035 va_end(ap);
1036 return plist;
1038 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void**)&v, (void**)&k)) {
1039 WMHashRemove(plist->d.dict, k);
1040 WMReleasePropList(k);
1041 WMReleasePropList(v);
1043 WMHashInsert(plist->d.dict, WMRetainPropList(nkey),
1044 WMRetainPropList(nvalue));
1049 WMPropList*
1050 WMRetainPropList(WMPropList *plist)
1052 WMPropList *key, *value;
1053 WMHashEnumerator e;
1054 int i;
1056 plist->retainCount++;
1058 switch(plist->type) {
1059 case WPLString:
1060 case WPLData:
1061 break;
1062 case WPLArray:
1063 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1064 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1066 break;
1067 case WPLDictionary:
1068 e = WMEnumerateHashTable(plist->d.dict);
1069 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1070 WMRetainPropList(key);
1071 WMRetainPropList(value);
1073 break;
1074 default:
1075 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1076 wassertrv(False, NULL);
1077 break;
1080 return plist;
1084 void
1085 WMReleasePropList(WMPropList *plist)
1087 WMPropList *key, *value;
1088 WMHashEnumerator e;
1089 int i;
1091 plist->retainCount--;
1093 switch(plist->type) {
1094 case WPLString:
1095 if (plist->retainCount < 1) {
1096 wfree(plist->d.string);
1097 wfree(plist);
1099 break;
1100 case WPLData:
1101 if (plist->retainCount < 1) {
1102 WMReleaseData(plist->d.data);
1103 wfree(plist);
1105 break;
1106 case WPLArray:
1107 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1108 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1110 if (plist->retainCount < 1) {
1111 WMFreeArray(plist->d.array);
1112 wfree(plist);
1114 break;
1115 case WPLDictionary:
1116 e = WMEnumerateHashTable(plist->d.dict);
1117 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1118 WMReleasePropList(key);
1119 WMReleasePropList(value);
1121 if (plist->retainCount < 1) {
1122 WMFreeHashTable(plist->d.dict);
1123 wfree(plist);
1125 break;
1126 default:
1127 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1128 wassertr(False);
1129 break;
1134 void
1135 WMInsertInPLArray(WMPropList *plist, int index, WMPropList *item)
1137 wassertr(plist->type==WPLArray);
1139 retainPropListByCount(item, plist->retainCount);
1140 WMInsertInArray(plist->d.array, index, item);
1144 void
1145 WMAddToPLArray(WMPropList *plist, WMPropList *item)
1147 wassertr(plist->type==WPLArray);
1149 retainPropListByCount(item, plist->retainCount);
1150 WMAddToArray(plist->d.array, item);
1154 void
1155 WMDeleteFromPLArray(WMPropList *plist, int index)
1157 WMPropList *item;
1159 wassertr(plist->type==WPLArray);
1161 item = WMGetFromArray(plist->d.array, index);
1162 if (item != NULL) {
1163 WMDeleteFromArray(plist->d.array, index);
1164 releasePropListByCount(item, plist->retainCount);
1169 void
1170 WMRemoveFromPLArray(WMPropList *plist, WMPropList *item)
1172 WMPropList *iPtr;
1173 int i;
1175 wassertr(plist->type==WPLArray);
1177 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1178 iPtr = WMGetFromArray(plist->d.array, i);
1179 if (WMIsPropListEqualTo(item, iPtr)) {
1180 WMDeleteFromArray(plist->d.array, i);
1181 releasePropListByCount(iPtr, plist->retainCount);
1182 break;
1188 void
1189 WMPutInPLDictionary(WMPropList *plist, WMPropList *key, WMPropList *value)
1191 wassertr(plist->type==WPLDictionary);
1193 /*WMRetainPropList(key);*/
1194 WMRemoveFromPLDictionary(plist, key);
1195 retainPropListByCount(key, plist->retainCount);
1196 retainPropListByCount(value, plist->retainCount);
1197 WMHashInsert(plist->d.dict, key, value);
1198 /*WMReleasePropList(key);*/
1202 void
1203 WMRemoveFromPLDictionary(WMPropList *plist, WMPropList *key)
1205 WMPropList *k, *v;
1207 wassertr(plist->type==WPLDictionary);
1209 if (WMHashGetItemAndKey(plist->d.dict, key, (void**)&v, (void**)&k)) {
1210 WMHashRemove(plist->d.dict, k);
1211 releasePropListByCount(k, plist->retainCount);
1212 releasePropListByCount(v, plist->retainCount);
1217 WMPropList*
1218 WMMergePLDictionaries(WMPropList *dest, WMPropList *source, Bool recursive)
1220 WMPropList *key, *value, *dvalue;
1221 WMHashEnumerator e;
1223 wassertr(source->type==WPLDictionary && dest->type==WPLDictionary);
1225 if (source == dest)
1226 return dest;
1228 e = WMEnumerateHashTable(source->d.dict);
1229 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1230 if (recursive && value->type==WPLDictionary) {
1231 dvalue = WMHashGet(dest->d.dict, key);
1232 if (dvalue && dvalue->type==WPLDictionary) {
1233 WMMergePLDictionaries(dvalue, value, True);
1234 } else {
1235 WMPutInPLDictionary(dest, key, value);
1237 } else {
1238 WMPutInPLDictionary(dest, key, value);
1242 return dest;
1246 WMPropList*
1247 WMSubtractPLDictionaries(WMPropList *dest, WMPropList *source, Bool recursive)
1249 WMPropList *key, *value, *dvalue;
1250 WMHashEnumerator e;
1252 wassertr(source->type==WPLDictionary && dest->type==WPLDictionary);
1254 if (source == dest) {
1255 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1256 int i;
1258 for (i=0; i<WMGetArrayItemCount(keys->d.array); i++) {
1259 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1261 return dest;
1264 e = WMEnumerateHashTable(source->d.dict);
1265 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1266 dvalue = WMHashGet(dest->d.dict, key);
1267 if (!dvalue)
1268 continue;
1269 if (WMIsPropListEqualTo(value, dvalue)) {
1270 WMRemoveFromPLDictionary(dest, key);
1271 } else if (recursive && value->type==WPLDictionary &&
1272 dvalue->type==WPLDictionary) {
1273 WMSubtractPLDictionaries(dvalue, value, True);
1277 return dest;
1282 WMGetPropListItemCount(WMPropList *plist)
1284 switch(plist->type) {
1285 case WPLString:
1286 case WPLData:
1287 return 0; /* should this be 1 instead? */
1288 case WPLArray:
1289 return WMGetArrayItemCount(plist->d.array);
1290 case WPLDictionary:
1291 return (int)WMCountHashTable(plist->d.dict);
1292 default:
1293 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1294 wassertrv(False, 0);
1295 break;
1298 return 0;
1302 Bool
1303 WMIsPLString(WMPropList *plist)
1305 return (plist->type == WPLString);
1309 Bool
1310 WMIsPLData(WMPropList *plist)
1312 return (plist->type == WPLData);
1316 Bool
1317 WMIsPLArray(WMPropList *plist)
1319 return (plist->type == WPLArray);
1323 Bool
1324 WMIsPLDictionary(WMPropList *plist)
1326 return (plist->type == WPLDictionary);
1330 Bool
1331 WMIsPropListEqualTo(WMPropList *plist, WMPropList *other)
1333 WMPropList *key1, *item1, *item2;
1334 WMHashEnumerator enumerator;
1335 int n, i;
1337 if (plist->type != other->type)
1338 return False;
1340 switch(plist->type) {
1341 case WPLString:
1342 if (caseSensitive) {
1343 return (strcmp(plist->d.string, other->d.string) == 0);
1344 } else {
1345 return (strcasecmp(plist->d.string, other->d.string) == 0);
1347 case WPLData:
1348 return WMIsDataEqualToData(plist->d.data, other->d.data);
1349 case WPLArray:
1350 n = WMGetArrayItemCount(plist->d.array);
1351 if (n != WMGetArrayItemCount(other->d.array))
1352 return False;
1353 for (i=0; i<n; i++) {
1354 item1 = WMGetFromArray(plist->d.array, i);
1355 item2 = WMGetFromArray(other->d.array, i);
1356 if (!WMIsPropListEqualTo(item1, item2))
1357 return False;
1359 return True;
1360 case WPLDictionary:
1361 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1362 return False;
1363 enumerator = WMEnumerateHashTable(plist->d.dict);
1364 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void**)&item1,
1365 (void**)&key1)) {
1366 item2 = WMHashGet(other->d.dict, key1);
1367 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1368 return False;
1370 return True;
1371 default:
1372 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1373 wassertrv(False, False);
1374 break;
1377 return False;
1381 char*
1382 WMGetFromPLString(WMPropList *plist)
1384 wassertrv(plist->type==WPLString, NULL);
1386 return plist->d.string;
1390 WMData*
1391 WMGetFromPLData(WMPropList *plist)
1393 wassertrv(plist->type==WPLData, NULL);
1395 return plist->d.data;
1399 const unsigned char*
1400 WMGetPLDataBytes(WMPropList *plist)
1402 wassertrv(plist->type==WPLData, NULL);
1404 return WMDataBytes(plist->d.data);
1409 WMGetPLDataLength(WMPropList *plist)
1411 wassertrv(plist->type==WPLData, 0);
1413 return WMGetDataLength(plist->d.data);
1417 WMPropList*
1418 WMGetFromPLArray(WMPropList *plist, int index)
1420 wassertrv(plist->type==WPLArray, NULL);
1422 return WMGetFromArray(plist->d.array, index);
1426 WMPropList*
1427 WMGetFromPLDictionary(WMPropList *plist, WMPropList *key)
1429 wassertrv(plist->type==WPLDictionary, NULL);
1431 return WMHashGet(plist->d.dict, key);
1435 WMPropList*
1436 WMGetPLDictionaryKeys(WMPropList *plist)
1438 WMPropList *array, *key;
1439 WMHashEnumerator enumerator;
1441 wassertrv(plist->type==WPLDictionary, NULL);
1443 array = (WMPropList*)wmalloc(sizeof(W_PropList));
1444 array->type = WPLArray;
1445 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1446 array->retainCount = 1;
1448 enumerator = WMEnumerateHashTable(plist->d.dict);
1449 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1450 WMAddToArray(array->d.array, WMRetainPropList(key));
1453 return array;
1457 WMPropList*
1458 WMShallowCopyPropList(WMPropList *plist)
1460 WMPropList *ret = NULL;
1461 WMPropList *key, *item;
1462 WMHashEnumerator e;
1463 WMData *data;
1464 int i;
1466 switch(plist->type) {
1467 case WPLString:
1468 ret = WMCreatePLString(plist->d.string);
1469 break;
1470 case WPLData:
1471 data = WMCreateDataWithData(plist->d.data);
1472 ret = WMCreatePLData(data);
1473 WMReleaseData(data);
1474 break;
1475 case WPLArray:
1476 ret = (WMPropList*)wmalloc(sizeof(W_PropList));
1477 ret->type = WPLArray;
1478 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1479 ret->retainCount = 1;
1481 for(i=0; i<WMGetArrayItemCount(ret->d.array); i++)
1482 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1484 break;
1485 case WPLDictionary:
1486 ret = WMCreatePLDictionary(NULL, NULL);
1487 e = WMEnumerateHashTable(plist->d.dict);
1488 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&item, (void**)&key)) {
1489 WMPutInPLDictionary(ret, key, item);
1491 break;
1492 default:
1493 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1494 wassertrv(False, NULL);
1495 break;
1498 return ret;
1502 WMPropList*
1503 WMDeepCopyPropList(WMPropList *plist)
1505 WMPropList *ret = NULL;
1506 WMPropList *key, *item;
1507 WMHashEnumerator e;
1508 WMData *data;
1509 int i;
1511 switch(plist->type) {
1512 case WPLString:
1513 ret = WMCreatePLString(plist->d.string);
1514 break;
1515 case WPLData:
1516 data = WMCreateDataWithData(plist->d.data);
1517 ret = WMCreatePLData(data);
1518 WMReleaseData(data);
1519 break;
1520 case WPLArray:
1521 ret = WMCreatePLArray(NULL);
1522 for(i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1523 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1524 WMAddToArray(ret->d.array, item);
1526 break;
1527 case WPLDictionary:
1528 ret = WMCreatePLDictionary(NULL, NULL);
1529 e = WMEnumerateHashTable(plist->d.dict);
1530 /* While we copy an existing dictionary there is no way that we can
1531 * have duplicate keys, so we don't need to first remove a key/value
1532 * pair before inserting the new key/value.
1534 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&item, (void**)&key)) {
1535 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key),
1536 WMDeepCopyPropList(item));
1538 break;
1539 default:
1540 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1541 wassertrv(False, NULL);
1542 break;
1545 return ret;
1549 WMPropList*
1550 WMCreatePropListFromDescription(char *desc)
1552 WMPropList *plist = NULL;
1553 PLData *pldata;
1555 pldata = (PLData*) wmalloc(sizeof(PLData));
1556 memset(pldata, 0, sizeof(PLData));
1557 pldata->ptr = desc;
1558 pldata->lineNumber = 1;
1560 plist = getPropList(pldata);
1562 if (getNonSpaceChar(pldata)!=0 && plist) {
1563 COMPLAIN(pldata, _("extra data after end of property list"));
1565 * We can't just ignore garbage after the end of the description
1566 * (especially if the description was read from a file), because
1567 * the "garbage" can be the real data and the real garbage is in
1568 * fact in the beginning of the file (which is now inside plist)
1570 WMReleasePropList(plist);
1571 plist = NULL;
1574 wfree(pldata);
1576 return plist;
1580 char*
1581 WMGetPropListDescription(WMPropList *plist, Bool indented)
1583 return (indented ? indentedDescription(plist, 0) : description(plist));
1587 WMPropList*
1588 WMReadPropListFromFile(char *file)
1590 WMPropList *plist = NULL;
1591 PLData *pldata;
1592 FILE *f;
1593 struct stat stbuf;
1594 size_t length;
1596 f = fopen(file, "rb");
1597 if (!f) {
1598 /* let the user print the error message if he really needs to */
1599 /*wsyserror(_("could not open domain file '%s' for reading"), file);*/
1600 return NULL;
1603 if (stat(file, &stbuf)==0) {
1604 length = (size_t) stbuf.st_size;
1605 } else {
1606 wsyserror(_("could not get size for file '%s'"), file);
1607 fclose(f);
1608 return NULL;
1611 pldata = (PLData*) wmalloc(sizeof(PLData));
1612 memset(pldata, 0, sizeof(PLData));
1613 pldata->ptr = (char*) wmalloc(length+1);
1614 pldata->filename = file;
1615 pldata->lineNumber = 1;
1617 if (fread(pldata->ptr, length, 1, f) != 1) {
1618 if (ferror(f)) {
1619 wsyserror(_("error reading from file '%s'"), file);
1621 plist = NULL;
1622 goto cleanup;
1625 pldata->ptr[length] = 0;
1627 plist = getPropList(pldata);
1629 if (getNonSpaceChar(pldata)!=0 && plist) {
1630 COMPLAIN(pldata, _("extra data after end of property list"));
1632 * We can't just ignore garbage after the end of the description
1633 * (especially if the description was read from a file), because
1634 * the "garbage" can be the real data and the real garbage is in
1635 * fact in the beginning of the file (which is now inside plist)
1637 WMReleasePropList(plist);
1638 plist = NULL;
1641 cleanup:
1642 wfree(pldata->ptr);
1643 wfree(pldata);
1644 fclose(f);
1646 return plist;
1650 /* TODO: review this function's code */
1652 Bool
1653 WMWritePropListToFile(WMPropList *plist, char *path, Bool atomically)
1655 char *thePath=NULL;
1656 char *desc;
1657 FILE *theFile;
1659 if (atomically) {
1660 #ifdef HAVE_MKSTEMP
1661 int fd, mask;
1662 #endif
1664 /* Use the path name of the destination file as a prefix for the
1665 * mkstemp() call so that we can be sure that both files are on
1666 * the same filesystem and the subsequent rename() will work. */
1667 thePath = wstrconcat(path, ".XXXXXX");
1669 #ifdef HAVE_MKSTEMP
1670 if ((fd = mkstemp(thePath)) < 0) {
1671 wsyserror(_("mkstemp (%s) failed"), thePath);
1672 goto failure;
1674 mask = umask(0);
1675 umask(mask);
1676 fchmod(fd, 0644 & ~mask);
1677 if ((theFile = fdopen(fd, "wb")) == NULL) {
1678 close(fd);
1680 #else
1681 if (mktemp(thePath) == NULL) {
1682 wsyserror(_("mktemp (%s) failed"), thePath);
1683 goto failure;
1685 theFile = fopen(thePath, "wb");
1686 #endif
1687 } else {
1688 thePath = wstrdup(path);
1689 theFile = fopen(thePath, "wb");
1692 if (theFile == NULL) {
1693 wsyserror(_("open (%s) failed"), thePath);
1694 goto failure;
1697 desc = indentedDescription(plist, 0);
1699 if (fprintf(theFile, "%s\n", desc) != strlen(desc)+1) {
1700 wsyserror(_("writing to file: %s failed"), thePath);
1701 wfree(desc);
1702 goto failure;
1705 wfree(desc);
1707 if (fclose(theFile) != 0) {
1708 wsyserror(_("fclose (%s) failed"), thePath);
1709 goto failure;
1712 /* If we used a temporary file, we still need to rename() it be the
1713 * real file. Also, we need to try to retain the file attributes of
1714 * the original file we are overwriting (if we are) */
1715 if (atomically) {
1716 if (rename(thePath, path) != 0) {
1717 wsyserror(_("rename ('%s' to '%s') failed"), thePath, path);
1718 goto failure;
1722 wfree(thePath);
1723 return True;
1725 failure:
1726 if (atomically) {
1727 unlink(thePath);
1728 wfree(thePath);
1731 return False;