Finished the proplist code. It's working now, but function names will change
[wmaker-crm.git] / WINGs / proplist.c
blob754473afbae968574d92ef2b1495efe8dc27809b
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)WMPLIsEqualToPL,
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 switch (plist->type) {
413 case WPLString:
414 retstr = stringDescription(plist);
415 break;
416 case WPLData:
417 retstr = dataDescription(plist);
418 break;
419 case WPLArray:
420 retstr = wstrdup("(\n");
421 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
422 str = indentedDescription(WMGetFromArray(plist->d.array, i),
423 level+1);
424 if (i==0) {
425 tmp = (char*)wmalloc(2*(level+1)+strlen(retstr)+strlen(str)+1);
426 sprintf(tmp, "%s%*s%s", retstr, 2*(level+1), "", str);
427 wfree(retstr);
428 retstr = tmp;
429 } else {
430 tmp = (char*)wmalloc(2*(level+1)+strlen(retstr)+strlen(str)+3);
431 sprintf(tmp, "%s,\n%*s%s", retstr, 2*(level+1), "", str);
432 wfree(retstr);
433 retstr = tmp;
435 wfree(str);
437 tmp = (char*)wmalloc(strlen(retstr) + 2*level + 3);
438 sprintf(tmp, "%s\n%*s)", retstr, 2*level, "");
439 wfree(retstr);
440 retstr = tmp;
441 break;
442 case WPLDictionary:
443 retstr = wstrdup("{\n");
444 e = WMEnumerateHashTable(plist->d.dict);
445 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&val, (void**)&key)) {
446 skey = indentedDescription(key, level+1);
447 sval = indentedDescription(val, level+1);
448 tmp = (char*)wmalloc(2*(level+1) + strlen(retstr) + strlen(skey)
449 + strlen(sval) + 6);
450 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2*(level+1), "",
451 skey, sval);
452 wfree(skey);
453 wfree(sval);
454 wfree(retstr);
455 retstr = tmp;
457 tmp = (char*)wmalloc(strlen(retstr) + 2*level + 2);
458 sprintf(tmp, "%s%*s}", retstr, 2*level, "");
459 wfree(retstr);
460 retstr = tmp;
461 break;
462 default:
463 wwarning(_("Used proplist functions on non-WMPropLists objects"));
464 wassertrv(False, NULL);
465 break;
468 return retstr;
472 static INLINE int
473 getChar(PLData *pldata)
475 int c;
477 c = pldata->ptr[pldata->pos];
478 if (c==0) {
479 return 0;
482 pldata->pos++;
484 if (c == '\n')
485 pldata->lineNumber++;
487 return c;
491 static INLINE int
492 getNonSpaceChar(PLData *pldata)
494 int c;
496 while (1) {
497 c = pldata->ptr[pldata->pos];
498 if (c==0) {
499 break;
501 pldata->pos++;
502 if (c == '\n') {
503 pldata->lineNumber++;
504 } else if (!isspace(c)) {
505 break;
509 return c;
513 static char*
514 unescapestr(char *src)
516 char *dest = wmalloc(strlen(src)+1);
517 char *sPtr, *dPtr;
518 char ch;
521 for (sPtr=src, dPtr=dest; *sPtr; sPtr++, dPtr++) {
522 if(*sPtr != '\\') {
523 *dPtr = *sPtr;
524 } else {
525 ch = *(++sPtr);
526 if((ch>='0') && (ch<='3')) {
527 /* assume next 2 chars are octal too */
528 *dPtr = ((ch & 07) << 6);
529 *dPtr |= ((*(++sPtr)&07)<<3);
530 *dPtr |= *(++sPtr)&07;
531 } else {
532 switch(ch) {
533 case 'a' : *dPtr = '\a'; break;
534 case 'b' : *dPtr = '\b'; break;
535 case 't' : *dPtr = '\t'; break;
536 case 'r' : *dPtr = '\r'; break;
537 case 'n' : *dPtr = '\n'; break;
538 case 'v' : *dPtr = '\v'; break;
539 case 'f' : *dPtr = '\f'; break;
540 default : *dPtr = *sPtr;
546 *dPtr = 0;
548 return dest;
552 static WMPropList*
553 getPLString(PLData *pldata)
555 WMPropList *plist;
556 StringBuffer sBuf;
557 int ptr = 0;
558 int c;
560 sBuf.str = wmalloc(BUFFERSIZE);
561 sBuf.size = BUFFERSIZE;
563 while (1) {
564 c = getChar(pldata);
565 if (ISSTRINGABLE(c)) {
566 CHECK_BUFFER_SIZE(sBuf, ptr);
567 sBuf.str[ptr++] = c;
568 } else {
569 if (c != 0) {
570 pldata->pos--;
572 break;
576 sBuf.str[ptr] = 0;
578 if (ptr == 0) {
579 plist = NULL;
580 } else {
581 char *tmp = unescapestr(sBuf.str);
582 plist = WMPLCreateString(tmp);
583 wfree(tmp);
586 wfree(sBuf.str);
588 return plist;
592 static WMPropList*
593 getPLQString(PLData *pldata)
595 WMPropList *plist;
596 int ptr = 0, escaping = 0, ok = 1;
597 int c;
598 StringBuffer sBuf;
600 sBuf.str = wmalloc(BUFFERSIZE);
601 sBuf.size = BUFFERSIZE;
603 while (1) {
604 c = getChar(pldata);
605 if (!escaping) {
606 if (c == '\\') {
607 escaping = 1;
608 continue;
609 } else if (c == '"') {
610 break;
612 } else {
613 CHECK_BUFFER_SIZE(sBuf, ptr);
614 sBuf.str[ptr++] = '\\';
615 escaping = 0;
618 if (c == 0) {
619 COMPLAIN(pldata, _("unterminated PropList string"));
620 ok = 0;
621 break;
622 } else {
623 CHECK_BUFFER_SIZE(sBuf, ptr);
624 sBuf.str[ptr++] = c;
628 sBuf.str[ptr] = 0;
630 if (!ok) {
631 plist = NULL;
632 } else {
633 char *tmp = unescapestr(sBuf.str);
634 plist = WMPLCreateString(tmp);
635 wfree(tmp);
638 wfree(sBuf.str);
640 return plist;
644 static WMPropList*
645 getPLData(PLData *pldata)
647 int ok = 1;
648 int len = 0;
649 int c1, c2;
650 unsigned char buf[BUFFERSIZE], byte;
651 WMPropList *plist;
652 WMData *data;
654 data = WMCreateDataWithCapacity(0);
656 while (1) {
657 c1 = getNonSpaceChar(pldata);
658 if (c1 == 0) {
659 COMPLAIN(pldata, _("unterminated PropList data"));
660 ok = 0;
661 break;
662 } else if (c1=='>') {
663 break;
664 } else if (ishexdigit(c1)) {
665 c2 = getNonSpaceChar(pldata);
666 if (c2==0 || c2=='>') {
667 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
668 ok = 0;
669 break;
670 } else if (ishexdigit(c2)) {
671 byte = char2num(c1) << 4;
672 byte |= char2num(c2);
673 buf[len++] = byte;
674 if (len == sizeof(buf)) {
675 WMAppendDataBytes(data, buf, len);
676 len = 0;
678 } else {
679 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
680 ok = 0;
681 break;
686 if (!ok) {
687 WMReleaseData(data);
688 return NULL;
691 if (len > 0)
692 WMAppendDataBytes(data, buf, len);
694 plist = WMPLCreateData(data);
695 WMReleaseData(data);
697 return plist;
701 static WMPropList*
702 getPLArray(PLData *pldata)
704 Bool first = True;
705 int ok=1;
706 int c;
707 WMPropList *array, *obj;
709 array = WMPLCreateArray(NULL);
711 while (1) {
712 c = getNonSpaceChar(pldata);
713 if (c == 0) {
714 COMPLAIN(pldata, _("unterminated PropList array"));
715 ok = 0;
716 break;
717 } else if (c == ')') {
718 break;
719 } else if (c == ',') {
720 /* continue normally */
721 } else if (!first) {
722 COMPLAIN(pldata, _("missing , or unterminated PropList array"));
723 ok = 0;
724 break;
725 } else {
726 pldata->pos--;
728 first = False;
730 obj = getPropList(pldata);
731 if (!obj) {
732 COMPLAIN(pldata, _("could not get PropList array element"));
733 ok = 0;
734 break;
736 WMPLAddToArray(array, obj);
737 WMPLRelease(obj);
740 if (!ok) {
741 WMPLRelease(array);
742 array = NULL;
745 return array;
749 static WMPropList*
750 getPLDictionary(PLData *pldata)
752 int ok = 1;
753 int c;
754 WMPropList *dict, *key, *value;
756 dict = WMPLCreateDictionary(NULL, NULL);
758 while (1) {
759 c = getNonSpaceChar(pldata);
760 if (c==0) {
761 COMPLAIN(pldata, _("unterminated PropList dictionary"));
762 ok = 0;
763 break;
764 } else if (c=='}') {
765 break;
768 DPUT("getting PropList dictionary key");
769 if (c == '<') {
770 key = getPLData(pldata);
771 } else if (c == '"') {
772 key = getPLQString(pldata);
773 } else if (ISSTRINGABLE(c)) {
774 pldata->pos--;
775 key = getPLString(pldata);
776 } else {
777 if (c == '=') {
778 COMPLAIN(pldata, _("missing PropList dictionary key"));
779 } else {
780 COMPLAIN(pldata, _("missing PropList dictionary entry key "
781 "or unterminated dictionary"));
783 ok = 0;
784 break;
787 if (!key) {
788 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
789 ok = 0;
790 break;
793 c = getNonSpaceChar(pldata);
794 if (c != '=') {
795 WMPLRelease(key);
796 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
797 ok = 0;
798 break;
801 DPUT("getting PropList dictionary entry value for key");
802 value = getPropList(pldata);
803 if (!value) {
804 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
805 WMPLRelease(key);
806 ok = 0;
807 break;
810 c = getNonSpaceChar(pldata);
811 if (c != ';') {
812 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
813 WMPLRelease(key);
814 WMPLRelease(value);
815 ok = 0;
816 break;
819 WMPLPutInDictionary(dict, key, value);
820 WMPLRelease(key);
821 WMPLRelease(value);
824 if (!ok) {
825 WMPLRelease(dict);
826 dict = NULL;
829 return dict;
833 static WMPropList*
834 getPropList(PLData *pldata)
836 WMPropList *plist;
837 int c;
839 c = getNonSpaceChar(pldata);
841 switch(c) {
842 case 0:
843 DPUT("End of PropList");
844 plist = NULL;
845 break;
847 case '{':
848 DPUT("Getting PropList dictionary");
849 plist = getPLDictionary(pldata);
850 break;
852 case '(':
853 DPUT("Getting PropList srrsy");
854 plist = getPLArray(pldata);
855 break;
857 case '<':
858 DPUT("Getting PropList data");
859 plist = getPLData(pldata);
860 break;
862 case '"':
863 DPUT("Getting PropList quoted string");
864 plist = getPLQString(pldata);
865 break;
867 default:
868 if (ISSTRINGABLE(c)) {
869 DPUT("Getting PropList string");
870 pldata->pos--;
871 plist = getPLString(pldata);
872 } else {
873 COMPLAIN(pldata, _("was expecting a string, data, array or "
874 "dictionary. If it's a string, try enclosing "
875 "it with \"."));
876 if (c=='#' || c=='/') {
877 wwarning(_("Comments are not allowed inside WindowMaker owned"
878 " domain files."));
880 plist = NULL;
882 break;
885 return plist;
889 void
890 WMPLSetCaseSensitive(Bool useCase)
892 caseSensitive = useCase;
896 WMPropList*
897 WMPLCreateString(char *str)
899 WMPropList *plist;
901 wassertrv(str!=NULL, NULL);
903 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
905 plist->type = WPLString;
906 plist->d.string = wstrdup(str);
907 plist->retainCount = 1;
909 return plist;
913 WMPropList*
914 WMPLCreateData(WMData *data)
916 WMPropList *plist;
918 wassertrv(data!=NULL, NULL);
920 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
922 plist->type = WPLData;
923 plist->d.data = WMRetainData(data);
924 plist->retainCount = 1;
926 return plist;
930 WMPropList*
931 WMPLCreateDataWithBytes(unsigned char *bytes, unsigned int length)
933 WMPropList *plist;
935 wassertrv(bytes!=NULL, NULL);
937 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
939 plist->type = WPLData;
940 plist->d.data = WMCreateDataWithBytes(bytes, length);
941 plist->retainCount = 1;
943 return plist;
947 WMPropList*
948 WMPLCreateDataWithBytesNoCopy(unsigned char *bytes, unsigned int length,
949 WMFreeDataProc *destructor)
951 WMPropList *plist;
953 wassertrv(bytes!=NULL, NULL);
955 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
957 plist->type = WPLData;
958 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
959 plist->retainCount = 1;
961 return plist;
965 WMPropList*
966 WMPLCreateArray(WMPropList *elem, ...)
968 WMPropList *plist, *nelem;
969 va_list ap;
971 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
972 plist->type = WPLArray;
973 plist->d.array = WMCreateArray(4);
974 plist->retainCount = 1;
976 if (!elem)
977 return plist;
979 WMAddToArray(plist->d.array, WMPLRetain(elem));
981 va_start(ap, elem);
983 while (1) {
984 nelem = va_arg(ap, WMPropList*);
985 if(!nelem) {
986 va_end(ap);
987 return plist;
989 WMAddToArray(plist->d.array, WMPLRetain(nelem));
994 WMPropList*
995 WMPLCreateDictionary(WMPropList *key, WMPropList *value, ...)
997 WMPropList *plist, *nkey, *nvalue, *k, *v;
998 va_list ap;
1000 plist = (WMPropList*)wmalloc(sizeof(W_PropList));
1001 plist->type = WPLDictionary;
1002 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
1003 plist->retainCount = 1;
1005 if (!key || !value)
1006 return plist;
1008 WMHashInsert(plist->d.dict, WMPLRetain(key), WMPLRetain(value));
1010 va_start(ap, value);
1012 while (1) {
1013 nkey = va_arg(ap, WMPropList*);
1014 if (!nkey) {
1015 va_end(ap);
1016 return plist;
1018 nvalue = va_arg(ap, WMPropList*);
1019 if (!nvalue) {
1020 va_end(ap);
1021 return plist;
1023 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void**)&v, (void**)&k)) {
1024 WMHashRemove(plist->d.dict, k);
1025 WMPLRelease(k);
1026 WMPLRelease(v);
1028 WMHashInsert(plist->d.dict, WMPLRetain(nkey), WMPLRetain(nvalue));
1033 void
1034 WMPLInsertInArray(WMPropList *plist, int index, WMPropList *item)
1036 wassertr(plist->type==WPLArray);
1038 retainPropListByCount(item, plist->retainCount);
1039 WMInsertInArray(plist->d.array, index, item);
1043 void
1044 WMPLAddToArray(WMPropList *plist, WMPropList *item)
1046 wassertr(plist->type==WPLArray);
1048 retainPropListByCount(item, plist->retainCount);
1049 WMAddToArray(plist->d.array, item);
1053 void
1054 WMPLDeleteFromArray(WMPropList *plist, int index)
1056 WMPropList *item;
1058 wassertr(plist->type==WPLArray);
1060 item = WMGetFromArray(plist->d.array, index);
1061 if (item != NULL) {
1062 WMDeleteFromArray(plist->d.array, index);
1063 releasePropListByCount(item, plist->retainCount);
1068 void
1069 WMPLRemoveFromArray(WMPropList *plist, WMPropList *item)
1071 WMPropList *iPtr;
1072 int i;
1074 wassertr(plist->type==WPLArray);
1076 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1077 iPtr = WMGetFromArray(plist->d.array, i);
1078 if (WMPLIsEqualToPL(item, iPtr)) {
1079 WMDeleteFromArray(plist->d.array, i);
1080 releasePropListByCount(iPtr, plist->retainCount);
1081 break;
1087 void
1088 WMPLPutInDictionary(WMPropList *plist, WMPropList *key, WMPropList *value)
1090 wassertr(plist->type==WPLDictionary);
1092 WMPLRemoveFromDictionary(plist, key);
1093 retainPropListByCount(key, plist->retainCount);
1094 retainPropListByCount(value, plist->retainCount);
1095 WMHashInsert(plist->d.dict, key, value);
1099 void
1100 WMPLRemoveFromDictionary(WMPropList *plist, WMPropList *key)
1102 WMPropList *k, *v;
1104 wassertr(plist->type==WPLDictionary);
1106 if (WMHashGetItemAndKey(plist->d.dict, key, (void**)&v, (void**)&k)) {
1107 WMHashRemove(plist->d.dict, k);
1108 releasePropListByCount(k, plist->retainCount);
1109 releasePropListByCount(v, plist->retainCount);
1114 WMPropList*
1115 WMPLMergeDictionaries(WMPropList *dest, WMPropList *source)
1117 WMPropList *key, *value;
1118 WMHashEnumerator e;
1120 wassertr(source->type==WPLDictionary && dest->type==WPLDictionary);
1122 e = WMEnumerateHashTable(source->d.dict);
1123 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1124 WMPLPutInDictionary(dest, key, value);
1127 return dest;
1131 WMPropList*
1132 WMPLRetain(WMPropList *plist)
1134 WMPropList *key, *value;
1135 WMHashEnumerator e;
1136 int i;
1138 plist->retainCount++;
1140 switch(plist->type) {
1141 case WPLString:
1142 case WPLData:
1143 break;
1144 case WPLArray:
1145 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1146 WMPLRetain(WMGetFromArray(plist->d.array, i));
1148 break;
1149 case WPLDictionary:
1150 e = WMEnumerateHashTable(plist->d.dict);
1151 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1152 WMPLRetain(key);
1153 WMPLRetain(value);
1155 break;
1156 default:
1157 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1158 wassertrv(False, NULL);
1159 break;
1162 return plist;
1166 void
1167 WMPLRelease(WMPropList *plist)
1169 WMPropList *key, *value;
1170 WMHashEnumerator e;
1171 int i;
1173 plist->retainCount--;
1175 switch(plist->type) {
1176 case WPLString:
1177 if (plist->retainCount < 1) {
1178 wfree(plist->d.string);
1179 wfree(plist);
1181 break;
1182 case WPLData:
1183 if (plist->retainCount < 1) {
1184 WMReleaseData(plist->d.data);
1185 wfree(plist);
1187 break;
1188 case WPLArray:
1189 for (i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1190 WMPLRelease(WMGetFromArray(plist->d.array, i));
1192 if (plist->retainCount < 1) {
1193 WMFreeArray(plist->d.array);
1194 wfree(plist);
1196 break;
1197 case WPLDictionary:
1198 e = WMEnumerateHashTable(plist->d.dict);
1199 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&value, (void**)&key)) {
1200 WMPLRelease(key);
1201 WMPLRelease(value);
1203 if (plist->retainCount < 1) {
1204 WMFreeHashTable(plist->d.dict);
1205 wfree(plist);
1207 break;
1208 default:
1209 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1210 wassertr(False);
1211 break;
1216 Bool
1217 WMPLIsString(WMPropList *plist)
1219 return (plist->type == WPLString);
1223 Bool
1224 WMPLIsData(WMPropList *plist)
1226 return (plist->type == WPLData);
1230 Bool
1231 WMPLIsArray(WMPropList *plist)
1233 return (plist->type == WPLArray);
1237 Bool
1238 WMPLIsDictionary(WMPropList *plist)
1240 return (plist->type == WPLDictionary);
1244 Bool
1245 WMPLIsEqualToPL(WMPropList *plist, WMPropList *other)
1247 WMPropList *key1, *item1, *item2;
1248 WMHashEnumerator enumerator;
1249 int n, i;
1251 if (plist->type != other->type)
1252 return False;
1254 switch(plist->type) {
1255 case WPLString:
1256 if (caseSensitive) {
1257 return (strcmp(plist->d.string, other->d.string) == 0);
1258 } else {
1259 return (strcasecmp(plist->d.string, other->d.string) == 0);
1261 case WPLData:
1262 return WMIsDataEqualToData(plist->d.data, other->d.data);
1263 case WPLArray:
1264 n = WMGetArrayItemCount(plist->d.array);
1265 if (n != WMGetArrayItemCount(other->d.array))
1266 return False;
1267 for (i=0; i<n; i++) {
1268 item1 = WMGetFromArray(plist->d.array, i);
1269 item2 = WMGetFromArray(other->d.array, i);
1270 if (!WMPLIsEqualToPL(item1, item2))
1271 return False;
1273 return True;
1274 case WPLDictionary:
1275 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1276 return False;
1277 enumerator = WMEnumerateHashTable(plist->d.dict);
1278 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void**)&item1,
1279 (void**)&key1)) {
1280 item2 = WMHashGet(other->d.dict, key1);
1281 if (!item2 || !item1 || !WMPLIsEqualToPL(item1, item2))
1282 return False;
1284 return True;
1285 default:
1286 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1287 wassertrv(False, False);
1288 break;
1291 return False;
1296 WMPLGetItemCount(WMPropList *plist)
1298 switch(plist->type) {
1299 case WPLString:
1300 case WPLData:
1301 return 0; /* should this be 1 instead? */
1302 case WPLArray:
1303 return WMGetArrayItemCount(plist->d.array);
1304 case WPLDictionary:
1305 return (int)WMCountHashTable(plist->d.dict);
1306 default:
1307 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1308 wassertrv(False, 0);
1309 break;
1312 return 0;
1316 char*
1317 WMPLGetString(WMPropList *plist)
1319 wassertrv(plist->type==WPLString, NULL);
1321 return plist->d.string;
1325 WMData*
1326 WMPLGetData(WMPropList *plist)
1328 wassertrv(plist->type==WPLData, NULL);
1330 return plist->d.data;
1334 const unsigned char*
1335 WMPLGetDataBytes(WMPropList *plist)
1337 wassertrv(plist->type==WPLData, NULL);
1339 return WMDataBytes(plist->d.data);
1344 WMPLGetDataLength(WMPropList *plist)
1346 wassertrv(plist->type==WPLData, 0);
1348 return WMGetDataLength(plist->d.data);
1352 WMPropList*
1353 WMPLGetFromArray(WMPropList *plist, int index)
1355 wassertrv(plist->type==WPLArray, NULL);
1357 return WMGetFromArray(plist->d.array, index);
1361 WMPropList*
1362 WMPLGetFromDictionary(WMPropList *plist, WMPropList *key)
1364 wassertrv(plist->type==WPLDictionary, NULL);
1366 return WMHashGet(plist->d.dict, key);
1370 WMPropList*
1371 WMPLGetDictionaryKeys(WMPropList *plist)
1373 WMPropList *array, *key;
1374 WMHashEnumerator enumerator;
1376 wassertrv(plist->type==WPLDictionary, NULL);
1378 array = (WMPropList*)wmalloc(sizeof(W_PropList));
1379 array->type = WPLArray;
1380 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1381 array->retainCount = 1;
1383 enumerator = WMEnumerateHashTable(plist->d.dict);
1384 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1385 WMAddToArray(array->d.array, WMPLRetain(key));
1388 return array;
1392 char*
1393 WMPLGetDescription(WMPropList *plist, Bool indented)
1395 return (indented ? indentedDescription(plist, 0) : description(plist));
1400 /* TODO: review this function's code */
1402 Bool
1403 WMPLWriteToFile(WMPropList *plist, char *path, Bool atomically)
1405 char *thePath=NULL;
1406 char *desc;
1407 FILE *theFile;
1409 if (atomically) {
1410 #ifdef HAVE_MKSTEMP
1411 int fd, mask;
1412 #endif
1414 /* Use the path name of the destination file as a prefix for the
1415 * mkstemp() call so that we can be sure that both files are on
1416 * the same filesystem and the subsequent rename() will work. */
1417 thePath = wstrconcat(path, ".XXXXXX");
1419 #ifdef HAVE_MKSTEMP
1420 if ((fd = mkstemp(thePath)) < 0) {
1421 wsyserror(_("mkstemp (%s) failed"), thePath);
1422 goto failure;
1424 mask = umask(0);
1425 umask(mask);
1426 fchmod(fd, 0644 & ~mask);
1427 if ((theFile = fdopen(fd, "w")) == NULL) {
1428 close(fd);
1430 #else
1431 if (mktemp(thePath) == NULL) {
1432 wsyserror(_("mktemp (%s) failed"), thePath);
1433 goto failure;
1435 theFile = fopen(thePath, "wb");
1436 #endif
1437 } else {
1438 thePath = wstrdup(path);
1439 theFile = fopen(thePath, "wb");
1442 if (theFile == NULL) {
1443 wsyserror(_("open (%s) failed"), thePath);
1444 goto failure;
1447 desc = indentedDescription(plist, 0);
1449 if (fprintf(theFile, "%s\n", desc) != strlen(desc)+1) {
1450 wsyserror(_("writing to file: %s failed"), thePath);
1451 wfree(desc);
1452 goto failure;
1455 wfree(desc);
1457 if (fclose(theFile) != 0) {
1458 wsyserror(_("fclose (%s) failed"), thePath);
1459 goto failure;
1462 /* If we used a temporary file, we still need to rename() it be the
1463 * real file. Also, we need to try to retain the file attributes of
1464 * the original file we are overwriting (if we are) */
1465 if (atomically) {
1466 if (rename(thePath, path) != 0) {
1467 wsyserror(_("rename ('%s' to '%s') failed"), thePath, path);
1468 goto failure;
1472 wfree(thePath);
1473 return True;
1475 failure:
1476 if (atomically) {
1477 unlink(thePath);
1478 wfree(thePath);
1481 return False;
1485 WMPropList*
1486 WMPLShallowCopy(WMPropList *plist)
1488 WMPropList *ret = NULL;
1489 WMPropList *key, *item;
1490 WMHashEnumerator e;
1491 WMData *data;
1492 int i;
1494 switch(plist->type) {
1495 case WPLString:
1496 ret = WMPLCreateString(plist->d.string);
1497 break;
1498 case WPLData:
1499 data = WMCreateDataWithData(plist->d.data);
1500 ret = WMPLCreateData(data);
1501 WMReleaseData(data);
1502 break;
1503 case WPLArray:
1504 ret = (WMPropList*)wmalloc(sizeof(W_PropList));
1505 ret->type = WPLArray;
1506 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1507 ret->retainCount = 1;
1509 for(i=0; i<WMGetArrayItemCount(ret->d.array); i++)
1510 WMPLRetain(WMGetFromArray(ret->d.array, i));
1512 break;
1513 case WPLDictionary:
1514 ret = WMPLCreateDictionary(NULL, NULL);
1515 e = WMEnumerateHashTable(plist->d.dict);
1516 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&item, (void**)&key)) {
1517 WMPLPutInDictionary(ret, key, item);
1519 break;
1520 default:
1521 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1522 wassertrv(False, NULL);
1523 break;
1526 return ret;
1530 WMPropList*
1531 WMPLDuplicate(WMPropList *plist)
1533 WMPropList *ret = NULL;
1534 WMPropList *key, *item;
1535 WMHashEnumerator e;
1536 WMData *data;
1537 int i;
1539 switch(plist->type) {
1540 case WPLString:
1541 ret = WMPLCreateString(plist->d.string);
1542 break;
1543 case WPLData:
1544 data = WMCreateDataWithData(plist->d.data);
1545 ret = WMPLCreateData(data);
1546 WMReleaseData(data);
1547 break;
1548 case WPLArray:
1549 ret = WMPLCreateArray(NULL);
1550 for(i=0; i<WMGetArrayItemCount(plist->d.array); i++) {
1551 item = WMPLDuplicate(WMGetFromArray(plist->d.array, i));
1552 WMAddToArray(ret->d.array, item);
1554 break;
1555 case WPLDictionary:
1556 ret = WMPLCreateDictionary(NULL, NULL);
1557 e = WMEnumerateHashTable(plist->d.dict);
1558 /* While we copy an existing dictionary there is no way that we can
1559 * have duplicate keys, so we don't need to first remove a key/value
1560 * pair before inserting the new key/value.
1562 while (WMNextHashEnumeratorItemAndKey(&e, (void**)&item, (void**)&key)) {
1563 WMHashInsert(ret->d.dict, WMPLDuplicate(key), WMPLDuplicate(item));
1565 break;
1566 default:
1567 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1568 wassertrv(False, NULL);
1569 break;
1572 return ret;
1576 WMPropList*
1577 WMPLGetWithDescription(char *desc)
1579 WMPropList *plist = NULL;
1580 PLData *pldata;
1582 pldata = (PLData*) wmalloc(sizeof(PLData));
1583 memset(pldata, 0, sizeof(PLData));
1584 pldata->ptr = desc;
1585 pldata->lineNumber = 1;
1587 plist = getPropList(pldata);
1589 if (getNonSpaceChar(pldata)!=0 && plist) {
1590 COMPLAIN(pldata, _("extra data after end of property list"));
1592 * We can't just ignore garbage after the end of the description
1593 * (especially if the description was read from a file), because
1594 * the "garbage" can be the real data and the real garbage is in
1595 * fact in the beginning of the file (which is now inside plist)
1597 WMPLRelease(plist);
1598 plist = NULL;
1601 wfree(pldata);
1603 return plist;
1607 WMPropList*
1608 WMPLReadFromFile(char *file)
1610 WMPropList *plist = NULL;
1611 PLData *pldata;
1612 FILE *f;
1613 struct stat stbuf;
1614 size_t length;
1616 f = fopen(file, "r");
1617 if (!f) {
1618 wsyserror(_("could not open domain file %s for reading"), file);
1619 return NULL;
1622 if (stat(file, &stbuf)==0) {
1623 length = (size_t) stbuf.st_size;
1624 } else {
1625 wsyserror(_("could not get size for domain file %s"), file);
1626 fclose(f);
1627 return NULL;
1630 pldata = (PLData*) wmalloc(sizeof(PLData));
1631 memset(pldata, 0, sizeof(PLData));
1632 pldata->ptr = (char*) wmalloc(length+1);
1633 pldata->filename = file;
1634 pldata->lineNumber = 1;
1636 if (fread(pldata->ptr, length, 1, f) != 1) {
1637 wsyserror(_("error reading from file %s"), file);
1638 plist = NULL;
1639 goto cleanup;
1642 pldata->ptr[length] = 0;
1644 plist = getPropList(pldata);
1646 if (getNonSpaceChar(pldata)!=0 && plist) {
1647 COMPLAIN(pldata, _("extra data after end of property list"));
1649 * We can't just ignore garbage after the end of the description
1650 * (especially if the description was read from a file), because
1651 * the "garbage" can be the real data and the real garbage is in
1652 * fact in the beginning of the file (which is now inside plist)
1654 WMPLRelease(plist);
1655 plist = NULL;
1658 cleanup:
1659 wfree(pldata->ptr);
1660 wfree(pldata);
1661 fclose(f);
1663 return plist;