Change to the linux kernel coding style
[wmaker-crm.git] / WINGs / proplist.c
blob79bb2983d33f244d63a6c2ca4f83b4bf72355d64
2 #include <string.h>
3 #include <stdarg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <ctype.h>
11 #include "WUtil.h"
12 #include "wconfig.h"
14 typedef enum {
15 WPLString = 0x57504c01,
16 WPLData = 0x57504c02,
17 WPLArray = 0x57504c03,
18 WPLDictionary = 0x57504c04
19 } WPLType;
21 typedef struct W_PropList {
22 WPLType type;
24 union {
25 char *string;
26 WMData *data;
27 WMArray *array;
28 WMHashTable *dict;
29 } d;
31 int retainCount;
32 } W_PropList;
34 typedef struct PLData {
35 char *ptr;
36 int pos;
37 char *filename;
38 int lineNumber;
39 } PLData;
41 typedef struct StringBuffer {
42 char *str;
43 int size;
44 } StringBuffer;
46 static unsigned hashPropList(WMPropList * plist);
47 static WMPropList *getPLString(PLData * pldata);
48 static WMPropList *getPLQString(PLData * pldata);
49 static WMPropList *getPLData(PLData * pldata);
50 static WMPropList *getPLArray(PLData * pldata);
51 static WMPropList *getPLDictionary(PLData * pldata);
52 static WMPropList *getPropList(PLData * pldata);
54 typedef unsigned (*hashFunc) (const void *);
55 typedef Bool(*isEqualFunc) (const void *, const void *);
56 typedef void *(*retainFunc) (const void *);
57 typedef void (*releaseFunc) (const void *);
59 static const WMHashTableCallbacks WMPropListHashCallbacks = {
60 (hashFunc) hashPropList,
61 (isEqualFunc) WMIsPropListEqualTo,
62 (retainFunc) NULL,
63 (releaseFunc) NULL
66 static Bool caseSensitive = True;
68 #define BUFFERSIZE 8192
69 #define BUFFERSIZE_INCREMENT 1024
71 #if 0
72 # define DPUT(s) puts(s)
73 #else
74 # define DPUT(s)
75 #endif
77 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
78 (pld)->filename ? "file" : "PropList",\
79 (pld)->filename ? (pld)->filename : "description",\
80 (pld)->lineNumber, msg)
82 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
83 || (c)=='+')
85 #define CHECK_BUFFER_SIZE(buf, ptr) \
86 if ((ptr) >= (buf).size-1) {\
87 (buf).size += BUFFERSIZE_INCREMENT;\
88 (buf).str = wrealloc((buf).str, (buf).size);\
91 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
92 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
93 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
94 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
95 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
96 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
97 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
99 #define MaxHashLength 64
101 static unsigned hashPropList(WMPropList * plist)
103 unsigned ret = 0;
104 unsigned ctr = 0;
105 const char *key;
106 int i, len;
108 switch (plist->type) {
109 case WPLString:
110 key = plist->d.string;
111 len = WMIN(strlen(key), MaxHashLength);
112 for (i = 0; i < len; i++) {
113 ret ^= tolower(key[i]) << ctr;
114 ctr = (ctr + 1) % sizeof(char *);
116 /*while (*key) {
117 ret ^= tolower(*key++) << ctr;
118 ctr = (ctr + 1) % sizeof (char *);
119 } */
120 break;
122 case WPLData:
123 key = WMDataBytes(plist->d.data);
124 len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
125 for (i = 0; i < len; i++) {
126 ret ^= key[i] << ctr;
127 ctr = (ctr + 1) % sizeof(char *);
129 break;
131 default:
132 wwarning(_("Only string or data is supported for a proplist dictionary key"));
133 wassertrv(False, 0);
134 break;
137 return ret;
140 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
142 WMPropList *key, *value;
143 WMHashEnumerator e;
144 int i;
146 plist->retainCount += count;
148 switch (plist->type) {
149 case WPLString:
150 case WPLData:
151 break;
152 case WPLArray:
153 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
154 retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
156 break;
157 case WPLDictionary:
158 e = WMEnumerateHashTable(plist->d.dict);
159 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
160 retainPropListByCount(key, count);
161 retainPropListByCount(value, count);
163 break;
164 default:
165 wwarning(_("Used proplist functions on non-WMPropLists objects"));
166 wassertrv(False, NULL);
167 break;
170 return plist;
173 static void releasePropListByCount(WMPropList * plist, int count)
175 WMPropList *key, *value;
176 WMHashEnumerator e;
177 int i;
179 plist->retainCount -= count;
181 switch (plist->type) {
182 case WPLString:
183 if (plist->retainCount < 1) {
184 wfree(plist->d.string);
185 wfree(plist);
187 break;
188 case WPLData:
189 if (plist->retainCount < 1) {
190 WMReleaseData(plist->d.data);
191 wfree(plist);
193 break;
194 case WPLArray:
195 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
196 releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
198 if (plist->retainCount < 1) {
199 WMFreeArray(plist->d.array);
200 wfree(plist);
202 break;
203 case WPLDictionary:
204 e = WMEnumerateHashTable(plist->d.dict);
205 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
206 releasePropListByCount(key, count);
207 releasePropListByCount(value, count);
209 if (plist->retainCount < 1) {
210 WMFreeHashTable(plist->d.dict);
211 wfree(plist);
213 break;
214 default:
215 wwarning(_("Used proplist functions on non-WMPropLists objects"));
216 wassertr(False);
217 break;
221 static char *dataDescription(WMPropList * plist)
223 const unsigned char *data;
224 char *retVal;
225 int i, j, length;
227 data = WMDataBytes(plist->d.data);
228 length = WMGetDataLength(plist->d.data);
230 retVal = (char *)wmalloc(2 * length + length / 4 + 3);
232 retVal[0] = '<';
233 for (i = 0, j = 1; i < length; i++) {
234 retVal[j++] = num2char((data[i] >> 4) & 0x0f);
235 retVal[j++] = num2char(data[i] & 0x0f);
236 if ((i & 0x03) == 3 && i != length - 1) {
237 /* if we've just finished a 32-bit int, add a space */
238 retVal[j++] = ' ';
241 retVal[j++] = '>';
242 retVal[j] = '\0';
244 return retVal;
247 static char *stringDescription(WMPropList * plist)
249 const char *str;
250 char *retVal, *sPtr, *dPtr;
251 int len, quote;
252 unsigned char ch;
254 str = plist->d.string;
256 if (strlen(str) == 0) {
257 return wstrdup("\"\"");
260 /* FIXME: make this work with unichars. */
262 quote = 0;
263 sPtr = (char *)str;
264 len = 0;
265 while ((ch = *sPtr)) {
266 if (!noquote(ch)) {
267 quote = 1;
268 if (charesc(ch))
269 len++;
270 else if (numesc(ch))
271 len += 3;
273 sPtr++;
274 len++;
277 if (quote)
278 len += 2;
280 retVal = (char *)wmalloc(len + 1);
282 sPtr = (char *)str;
283 dPtr = retVal;
285 if (quote)
286 *dPtr++ = '"';
288 while ((ch = *sPtr)) {
289 if (charesc(ch)) {
290 *(dPtr++) = '\\';
291 switch (ch) {
292 case '\a':
293 *dPtr = 'a';
294 break;
295 case '\b':
296 *dPtr = 'b';
297 break;
298 case '\t':
299 *dPtr = 't';
300 break;
301 case '\n':
302 *dPtr = 'n';
303 break;
304 case '\v':
305 *dPtr = 'v';
306 break;
307 case '\f':
308 *dPtr = 'f';
309 break;
310 default:
311 *dPtr = ch; /* " or \ */
313 } else if (numesc(ch)) {
314 *(dPtr++) = '\\';
315 *(dPtr++) = '0' + ((ch >> 6) & 07);
316 *(dPtr++) = '0' + ((ch >> 3) & 07);
317 *dPtr = '0' + (ch & 07);
318 } else {
319 *dPtr = ch;
321 sPtr++;
322 dPtr++;
325 if (quote)
326 *dPtr++ = '"';
328 *dPtr = '\0';
330 return retVal;
333 static char *description(WMPropList * plist)
335 WMPropList *key, *val;
336 char *retstr = NULL;
337 char *str, *tmp, *skey, *sval;
338 WMHashEnumerator e;
339 int i;
341 switch (plist->type) {
342 case WPLString:
343 retstr = stringDescription(plist);
344 break;
345 case WPLData:
346 retstr = dataDescription(plist);
347 break;
348 case WPLArray:
349 retstr = wstrdup("(");
350 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
351 str = description(WMGetFromArray(plist->d.array, i));
352 if (i == 0) {
353 retstr = wstrappend(retstr, str);
354 } else {
355 tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
356 sprintf(tmp, "%s, %s", retstr, str);
357 wfree(retstr);
358 retstr = tmp;
360 wfree(str);
362 retstr = wstrappend(retstr, ")");
363 break;
364 case WPLDictionary:
365 retstr = wstrdup("{");
366 e = WMEnumerateHashTable(plist->d.dict);
367 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
368 skey = description(key);
369 sval = description(val);
370 tmp = (char *)wmalloc(strlen(retstr) + strlen(skey) + strlen(sval) + 5);
371 sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
372 wfree(skey);
373 wfree(sval);
374 wfree(retstr);
375 retstr = tmp;
377 retstr = wstrappend(retstr, "}");
378 break;
379 default:
380 wwarning(_("Used proplist functions on non-WMPropLists objects"));
381 wassertrv(False, NULL);
382 break;
385 return retstr;
388 static char *indentedDescription(WMPropList * plist, int level)
390 WMPropList *key, *val;
391 char *retstr = NULL;
392 char *str, *tmp, *skey, *sval;
393 WMHashEnumerator e;
394 int i;
396 if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
397 retstr = description(plist);
399 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
400 return retstr;
401 } else if (retstr) {
402 wfree(retstr);
403 retstr = NULL;
407 switch (plist->type) {
408 case WPLString:
409 retstr = stringDescription(plist);
410 break;
411 case WPLData:
412 retstr = dataDescription(plist);
413 break;
414 case WPLArray:
415 retstr = wstrdup("(\n");
416 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
417 str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
418 if (i == 0) {
419 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
420 sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
421 wfree(retstr);
422 retstr = tmp;
423 } else {
424 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
425 sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
426 wfree(retstr);
427 retstr = tmp;
429 wfree(str);
431 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
432 sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
433 wfree(retstr);
434 retstr = tmp;
435 break;
436 case WPLDictionary:
437 retstr = wstrdup("{\n");
438 e = WMEnumerateHashTable(plist->d.dict);
439 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
440 skey = indentedDescription(key, level + 1);
441 sval = indentedDescription(val, level + 1);
442 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(skey)
443 + strlen(sval) + 6);
444 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
445 wfree(skey);
446 wfree(sval);
447 wfree(retstr);
448 retstr = tmp;
450 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
451 sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
452 wfree(retstr);
453 retstr = tmp;
454 break;
455 default:
456 wwarning(_("Used proplist functions on non-WMPropLists objects"));
457 wassertrv(False, NULL);
458 break;
461 return retstr;
464 static INLINE int getChar(PLData * pldata)
466 int c;
468 c = pldata->ptr[pldata->pos];
469 if (c == 0) {
470 return 0;
473 pldata->pos++;
475 if (c == '\n')
476 pldata->lineNumber++;
478 return c;
481 static INLINE int getNonSpaceChar(PLData * pldata)
483 int c;
485 while (1) {
486 c = pldata->ptr[pldata->pos];
487 if (c == 0) {
488 break;
490 pldata->pos++;
491 if (c == '\n') {
492 pldata->lineNumber++;
493 } else if (!isspace(c)) {
494 break;
498 return c;
501 static char *unescapestr(char *src)
503 char *dest = wmalloc(strlen(src) + 1);
504 char *sPtr, *dPtr;
505 char ch;
507 for (sPtr = src, dPtr = dest; *sPtr; sPtr++, dPtr++) {
508 if (*sPtr != '\\') {
509 *dPtr = *sPtr;
510 } else {
511 ch = *(++sPtr);
512 if ((ch >= '0') && (ch <= '3')) {
513 /* assume next 2 chars are octal too */
514 *dPtr = ((ch & 07) << 6);
515 *dPtr |= ((*(++sPtr) & 07) << 3);
516 *dPtr |= *(++sPtr) & 07;
517 } else {
518 switch (ch) {
519 case 'a':
520 *dPtr = '\a';
521 break;
522 case 'b':
523 *dPtr = '\b';
524 break;
525 case 't':
526 *dPtr = '\t';
527 break;
528 case 'r':
529 *dPtr = '\r';
530 break;
531 case 'n':
532 *dPtr = '\n';
533 break;
534 case 'v':
535 *dPtr = '\v';
536 break;
537 case 'f':
538 *dPtr = '\f';
539 break;
540 default:
541 *dPtr = *sPtr;
547 *dPtr = 0;
549 return dest;
552 static WMPropList *getPLString(PLData * pldata)
554 WMPropList *plist;
555 StringBuffer sBuf;
556 int ptr = 0;
557 int c;
559 sBuf.str = wmalloc(BUFFERSIZE);
560 sBuf.size = BUFFERSIZE;
562 while (1) {
563 c = getChar(pldata);
564 if (ISSTRINGABLE(c)) {
565 CHECK_BUFFER_SIZE(sBuf, ptr);
566 sBuf.str[ptr++] = c;
567 } else {
568 if (c != 0) {
569 pldata->pos--;
571 break;
575 sBuf.str[ptr] = 0;
577 if (ptr == 0) {
578 plist = NULL;
579 } else {
580 char *tmp = unescapestr(sBuf.str);
581 plist = WMCreatePLString(tmp);
582 wfree(tmp);
585 wfree(sBuf.str);
587 return plist;
590 static WMPropList *getPLQString(PLData * pldata)
592 WMPropList *plist;
593 int ptr = 0, escaping = 0, ok = 1;
594 int c;
595 StringBuffer sBuf;
597 sBuf.str = wmalloc(BUFFERSIZE);
598 sBuf.size = BUFFERSIZE;
600 while (1) {
601 c = getChar(pldata);
602 if (!escaping) {
603 if (c == '\\') {
604 escaping = 1;
605 continue;
606 } else if (c == '"') {
607 break;
609 } else {
610 CHECK_BUFFER_SIZE(sBuf, ptr);
611 sBuf.str[ptr++] = '\\';
612 escaping = 0;
615 if (c == 0) {
616 COMPLAIN(pldata, _("unterminated PropList string"));
617 ok = 0;
618 break;
619 } else {
620 CHECK_BUFFER_SIZE(sBuf, ptr);
621 sBuf.str[ptr++] = c;
625 sBuf.str[ptr] = 0;
627 if (!ok) {
628 plist = NULL;
629 } else {
630 char *tmp = unescapestr(sBuf.str);
631 plist = WMCreatePLString(tmp);
632 wfree(tmp);
635 wfree(sBuf.str);
637 return plist;
640 static WMPropList *getPLData(PLData * pldata)
642 int ok = 1;
643 int len = 0;
644 int c1, c2;
645 unsigned char buf[BUFFERSIZE], byte;
646 WMPropList *plist;
647 WMData *data;
649 data = WMCreateDataWithCapacity(0);
651 while (1) {
652 c1 = getNonSpaceChar(pldata);
653 if (c1 == 0) {
654 COMPLAIN(pldata, _("unterminated PropList data"));
655 ok = 0;
656 break;
657 } else if (c1 == '>') {
658 break;
659 } else if (ishexdigit(c1)) {
660 c2 = getNonSpaceChar(pldata);
661 if (c2 == 0 || c2 == '>') {
662 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
663 ok = 0;
664 break;
665 } else if (ishexdigit(c2)) {
666 byte = char2num(c1) << 4;
667 byte |= char2num(c2);
668 buf[len++] = byte;
669 if (len == sizeof(buf)) {
670 WMAppendDataBytes(data, buf, len);
671 len = 0;
673 } else {
674 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
675 ok = 0;
676 break;
678 } else {
679 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
680 ok = 0;
681 break;
685 if (!ok) {
686 WMReleaseData(data);
687 return NULL;
690 if (len > 0)
691 WMAppendDataBytes(data, buf, len);
693 plist = WMCreatePLData(data);
694 WMReleaseData(data);
696 return plist;
699 static WMPropList *getPLArray(PLData * pldata)
701 Bool first = True;
702 int ok = 1;
703 int c;
704 WMPropList *array, *obj;
706 array = WMCreatePLArray(NULL);
708 while (1) {
709 c = getNonSpaceChar(pldata);
710 if (c == 0) {
711 COMPLAIN(pldata, _("unterminated PropList array"));
712 ok = 0;
713 break;
714 } else if (c == ')') {
715 break;
716 } else if (c == ',') {
717 /* continue normally */
718 } else if (!first) {
719 COMPLAIN(pldata, _("missing or unterminated PropList array"));
720 ok = 0;
721 break;
722 } else {
723 pldata->pos--;
725 first = False;
727 obj = getPropList(pldata);
728 if (!obj) {
729 COMPLAIN(pldata, _("could not get PropList array element"));
730 ok = 0;
731 break;
733 WMAddToPLArray(array, obj);
734 WMReleasePropList(obj);
737 if (!ok) {
738 WMReleasePropList(array);
739 array = NULL;
742 return array;
745 static WMPropList *getPLDictionary(PLData * pldata)
747 int ok = 1;
748 int c;
749 WMPropList *dict, *key, *value;
751 dict = WMCreatePLDictionary(NULL, NULL);
753 while (1) {
754 c = getNonSpaceChar(pldata);
755 if (c == 0) {
756 COMPLAIN(pldata, _("unterminated PropList dictionary"));
757 ok = 0;
758 break;
759 } else if (c == '}') {
760 break;
763 DPUT("getting PropList dictionary key");
764 if (c == '<') {
765 key = getPLData(pldata);
766 } else if (c == '"') {
767 key = getPLQString(pldata);
768 } else if (ISSTRINGABLE(c)) {
769 pldata->pos--;
770 key = getPLString(pldata);
771 } else {
772 if (c == '=') {
773 COMPLAIN(pldata, _("missing PropList dictionary key"));
774 } else {
775 COMPLAIN(pldata, _("missing PropList dictionary entry key "
776 "or unterminated dictionary"));
778 ok = 0;
779 break;
782 if (!key) {
783 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
784 ok = 0;
785 break;
788 c = getNonSpaceChar(pldata);
789 if (c != '=') {
790 WMReleasePropList(key);
791 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
792 ok = 0;
793 break;
796 DPUT("getting PropList dictionary entry value for key");
797 value = getPropList(pldata);
798 if (!value) {
799 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
800 WMReleasePropList(key);
801 ok = 0;
802 break;
805 c = getNonSpaceChar(pldata);
806 if (c != ';') {
807 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
808 WMReleasePropList(key);
809 WMReleasePropList(value);
810 ok = 0;
811 break;
814 WMPutInPLDictionary(dict, key, value);
815 WMReleasePropList(key);
816 WMReleasePropList(value);
819 if (!ok) {
820 WMReleasePropList(dict);
821 dict = NULL;
824 return dict;
827 static WMPropList *getPropList(PLData * pldata)
829 WMPropList *plist;
830 int c;
832 c = getNonSpaceChar(pldata);
834 switch (c) {
835 case 0:
836 DPUT("End of PropList");
837 plist = NULL;
838 break;
840 case '{':
841 DPUT("Getting PropList dictionary");
842 plist = getPLDictionary(pldata);
843 break;
845 case '(':
846 DPUT("Getting PropList array");
847 plist = getPLArray(pldata);
848 break;
850 case '<':
851 DPUT("Getting PropList data");
852 plist = getPLData(pldata);
853 break;
855 case '"':
856 DPUT("Getting PropList quoted string");
857 plist = getPLQString(pldata);
858 break;
860 default:
861 if (ISSTRINGABLE(c)) {
862 DPUT("Getting PropList string");
863 pldata->pos--;
864 plist = getPLString(pldata);
865 } else {
866 COMPLAIN(pldata, _("was expecting a string, data, array or "
867 "dictionary. If it's a string, try enclosing " "it with \"."));
868 if (c == '#' || c == '/') {
869 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
871 plist = NULL;
873 break;
876 return plist;
879 void WMPLSetCaseSensitive(Bool caseSensitiveness)
881 caseSensitive = caseSensitiveness;
884 WMPropList *WMCreatePLString(char *str)
886 WMPropList *plist;
888 wassertrv(str != NULL, NULL);
890 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
892 plist->type = WPLString;
893 plist->d.string = wstrdup(str);
894 plist->retainCount = 1;
896 return plist;
899 WMPropList *WMCreatePLData(WMData * data)
901 WMPropList *plist;
903 wassertrv(data != NULL, NULL);
905 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
907 plist->type = WPLData;
908 plist->d.data = WMRetainData(data);
909 plist->retainCount = 1;
911 return plist;
914 WMPropList *WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
916 WMPropList *plist;
918 wassertrv(bytes != NULL, NULL);
920 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
922 plist->type = WPLData;
923 plist->d.data = WMCreateDataWithBytes(bytes, length);
924 plist->retainCount = 1;
926 return plist;
929 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
931 WMPropList *plist;
933 wassertrv(bytes != NULL, NULL);
935 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
937 plist->type = WPLData;
938 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
939 plist->retainCount = 1;
941 return plist;
944 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
946 WMPropList *plist, *nelem;
947 va_list ap;
949 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
950 plist->type = WPLArray;
951 plist->d.array = WMCreateArray(4);
952 plist->retainCount = 1;
954 if (!elem)
955 return plist;
957 WMAddToArray(plist->d.array, WMRetainPropList(elem));
959 va_start(ap, elem);
961 while (1) {
962 nelem = va_arg(ap, WMPropList *);
963 if (!nelem) {
964 va_end(ap);
965 return plist;
967 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
971 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
973 WMPropList *plist, *nkey, *nvalue, *k, *v;
974 va_list ap;
976 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
977 plist->type = WPLDictionary;
978 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
979 plist->retainCount = 1;
981 if (!key || !value)
982 return plist;
984 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
986 va_start(ap, value);
988 while (1) {
989 nkey = va_arg(ap, WMPropList *);
990 if (!nkey) {
991 va_end(ap);
992 return plist;
994 nvalue = va_arg(ap, WMPropList *);
995 if (!nvalue) {
996 va_end(ap);
997 return plist;
999 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
1000 WMHashRemove(plist->d.dict, k);
1001 WMReleasePropList(k);
1002 WMReleasePropList(v);
1004 WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
1008 WMPropList *WMRetainPropList(WMPropList * plist)
1010 WMPropList *key, *value;
1011 WMHashEnumerator e;
1012 int i;
1014 plist->retainCount++;
1016 switch (plist->type) {
1017 case WPLString:
1018 case WPLData:
1019 break;
1020 case WPLArray:
1021 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1022 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1024 break;
1025 case WPLDictionary:
1026 e = WMEnumerateHashTable(plist->d.dict);
1027 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1028 WMRetainPropList(key);
1029 WMRetainPropList(value);
1031 break;
1032 default:
1033 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1034 wassertrv(False, NULL);
1035 break;
1038 return plist;
1041 void WMReleasePropList(WMPropList * plist)
1043 WMPropList *key, *value;
1044 WMHashEnumerator e;
1045 int i;
1047 plist->retainCount--;
1049 switch (plist->type) {
1050 case WPLString:
1051 if (plist->retainCount < 1) {
1052 wfree(plist->d.string);
1053 wfree(plist);
1055 break;
1056 case WPLData:
1057 if (plist->retainCount < 1) {
1058 WMReleaseData(plist->d.data);
1059 wfree(plist);
1061 break;
1062 case WPLArray:
1063 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1064 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1066 if (plist->retainCount < 1) {
1067 WMFreeArray(plist->d.array);
1068 wfree(plist);
1070 break;
1071 case WPLDictionary:
1072 e = WMEnumerateHashTable(plist->d.dict);
1073 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1074 WMReleasePropList(key);
1075 WMReleasePropList(value);
1077 if (plist->retainCount < 1) {
1078 WMFreeHashTable(plist->d.dict);
1079 wfree(plist);
1081 break;
1082 default:
1083 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1084 wassertr(False);
1085 break;
1089 void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
1091 wassertr(plist->type == WPLArray);
1093 retainPropListByCount(item, plist->retainCount);
1094 WMInsertInArray(plist->d.array, index, item);
1097 void WMAddToPLArray(WMPropList * plist, WMPropList * item)
1099 wassertr(plist->type == WPLArray);
1101 retainPropListByCount(item, plist->retainCount);
1102 WMAddToArray(plist->d.array, item);
1105 void WMDeleteFromPLArray(WMPropList * plist, int index)
1107 WMPropList *item;
1109 wassertr(plist->type == WPLArray);
1111 item = WMGetFromArray(plist->d.array, index);
1112 if (item != NULL) {
1113 WMDeleteFromArray(plist->d.array, index);
1114 releasePropListByCount(item, plist->retainCount);
1118 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1120 WMPropList *iPtr;
1121 int i;
1123 wassertr(plist->type == WPLArray);
1125 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1126 iPtr = WMGetFromArray(plist->d.array, i);
1127 if (WMIsPropListEqualTo(item, iPtr)) {
1128 WMDeleteFromArray(plist->d.array, i);
1129 releasePropListByCount(iPtr, plist->retainCount);
1130 break;
1135 void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
1137 wassertr(plist->type == WPLDictionary);
1139 /*WMRetainPropList(key); */
1140 WMRemoveFromPLDictionary(plist, key);
1141 retainPropListByCount(key, plist->retainCount);
1142 retainPropListByCount(value, plist->retainCount);
1143 WMHashInsert(plist->d.dict, key, value);
1144 /*WMReleasePropList(key); */
1147 void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
1149 WMPropList *k, *v;
1151 wassertr(plist->type == WPLDictionary);
1153 if (WMHashGetItemAndKey(plist->d.dict, key, (void **)&v, (void **)&k)) {
1154 WMHashRemove(plist->d.dict, k);
1155 releasePropListByCount(k, plist->retainCount);
1156 releasePropListByCount(v, plist->retainCount);
1160 WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1162 WMPropList *key, *value, *dvalue;
1163 WMHashEnumerator e;
1165 wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1167 if (source == dest)
1168 return dest;
1170 e = WMEnumerateHashTable(source->d.dict);
1171 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1172 if (recursive && value->type == WPLDictionary) {
1173 dvalue = WMHashGet(dest->d.dict, key);
1174 if (dvalue && dvalue->type == WPLDictionary) {
1175 WMMergePLDictionaries(dvalue, value, True);
1176 } else {
1177 WMPutInPLDictionary(dest, key, value);
1179 } else {
1180 WMPutInPLDictionary(dest, key, value);
1184 return dest;
1187 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1189 WMPropList *key, *value, *dvalue;
1190 WMHashEnumerator e;
1192 wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1194 if (source == dest) {
1195 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1196 int i;
1198 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1199 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1201 return dest;
1204 e = WMEnumerateHashTable(source->d.dict);
1205 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1206 dvalue = WMHashGet(dest->d.dict, key);
1207 if (!dvalue)
1208 continue;
1209 if (WMIsPropListEqualTo(value, dvalue)) {
1210 WMRemoveFromPLDictionary(dest, key);
1211 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1212 WMSubtractPLDictionaries(dvalue, value, True);
1216 return dest;
1219 int WMGetPropListItemCount(WMPropList * plist)
1221 switch (plist->type) {
1222 case WPLString:
1223 case WPLData:
1224 return 0; /* should this be 1 instead? */
1225 case WPLArray:
1226 return WMGetArrayItemCount(plist->d.array);
1227 case WPLDictionary:
1228 return (int)WMCountHashTable(plist->d.dict);
1229 default:
1230 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1231 wassertrv(False, 0);
1232 break;
1235 return 0;
1238 Bool WMIsPLString(WMPropList * plist)
1240 return (plist->type == WPLString);
1243 Bool WMIsPLData(WMPropList * plist)
1245 return (plist->type == WPLData);
1248 Bool WMIsPLArray(WMPropList * plist)
1250 return (plist->type == WPLArray);
1253 Bool WMIsPLDictionary(WMPropList * plist)
1255 return (plist->type == WPLDictionary);
1258 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1260 WMPropList *key1, *item1, *item2;
1261 WMHashEnumerator enumerator;
1262 int n, i;
1264 if (plist->type != other->type)
1265 return False;
1267 switch (plist->type) {
1268 case WPLString:
1269 if (caseSensitive) {
1270 return (strcmp(plist->d.string, other->d.string) == 0);
1271 } else {
1272 return (strcasecmp(plist->d.string, other->d.string) == 0);
1274 case WPLData:
1275 return WMIsDataEqualToData(plist->d.data, other->d.data);
1276 case WPLArray:
1277 n = WMGetArrayItemCount(plist->d.array);
1278 if (n != WMGetArrayItemCount(other->d.array))
1279 return False;
1280 for (i = 0; i < n; i++) {
1281 item1 = WMGetFromArray(plist->d.array, i);
1282 item2 = WMGetFromArray(other->d.array, i);
1283 if (!WMIsPropListEqualTo(item1, item2))
1284 return False;
1286 return True;
1287 case WPLDictionary:
1288 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1289 return False;
1290 enumerator = WMEnumerateHashTable(plist->d.dict);
1291 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1292 item2 = WMHashGet(other->d.dict, key1);
1293 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1294 return False;
1296 return True;
1297 default:
1298 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1299 wassertrv(False, False);
1300 break;
1303 return False;
1306 char *WMGetFromPLString(WMPropList * plist)
1308 wassertrv(plist->type == WPLString, NULL);
1310 return plist->d.string;
1313 WMData *WMGetFromPLData(WMPropList * plist)
1315 wassertrv(plist->type == WPLData, NULL);
1317 return plist->d.data;
1320 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1322 wassertrv(plist->type == WPLData, NULL);
1324 return WMDataBytes(plist->d.data);
1327 int WMGetPLDataLength(WMPropList * plist)
1329 wassertrv(plist->type == WPLData, 0);
1331 return WMGetDataLength(plist->d.data);
1334 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1336 wassertrv(plist->type == WPLArray, NULL);
1338 return WMGetFromArray(plist->d.array, index);
1341 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1343 wassertrv(plist->type == WPLDictionary, NULL);
1345 return WMHashGet(plist->d.dict, key);
1348 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1350 WMPropList *array, *key;
1351 WMHashEnumerator enumerator;
1353 wassertrv(plist->type == WPLDictionary, NULL);
1355 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1356 array->type = WPLArray;
1357 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1358 array->retainCount = 1;
1360 enumerator = WMEnumerateHashTable(plist->d.dict);
1361 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1362 WMAddToArray(array->d.array, WMRetainPropList(key));
1365 return array;
1368 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1370 WMPropList *ret = NULL;
1371 WMPropList *key, *item;
1372 WMHashEnumerator e;
1373 WMData *data;
1374 int i;
1376 switch (plist->type) {
1377 case WPLString:
1378 ret = WMCreatePLString(plist->d.string);
1379 break;
1380 case WPLData:
1381 data = WMCreateDataWithData(plist->d.data);
1382 ret = WMCreatePLData(data);
1383 WMReleaseData(data);
1384 break;
1385 case WPLArray:
1386 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1387 ret->type = WPLArray;
1388 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1389 ret->retainCount = 1;
1391 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1392 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1394 break;
1395 case WPLDictionary:
1396 ret = WMCreatePLDictionary(NULL, NULL);
1397 e = WMEnumerateHashTable(plist->d.dict);
1398 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1399 WMPutInPLDictionary(ret, key, item);
1401 break;
1402 default:
1403 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1404 wassertrv(False, NULL);
1405 break;
1408 return ret;
1411 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1413 WMPropList *ret = NULL;
1414 WMPropList *key, *item;
1415 WMHashEnumerator e;
1416 WMData *data;
1417 int i;
1419 switch (plist->type) {
1420 case WPLString:
1421 ret = WMCreatePLString(plist->d.string);
1422 break;
1423 case WPLData:
1424 data = WMCreateDataWithData(plist->d.data);
1425 ret = WMCreatePLData(data);
1426 WMReleaseData(data);
1427 break;
1428 case WPLArray:
1429 ret = WMCreatePLArray(NULL);
1430 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1431 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1432 WMAddToArray(ret->d.array, item);
1434 break;
1435 case WPLDictionary:
1436 ret = WMCreatePLDictionary(NULL, NULL);
1437 e = WMEnumerateHashTable(plist->d.dict);
1438 /* While we copy an existing dictionary there is no way that we can
1439 * have duplicate keys, so we don't need to first remove a key/value
1440 * pair before inserting the new key/value.
1442 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1443 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1445 break;
1446 default:
1447 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1448 wassertrv(False, NULL);
1449 break;
1452 return ret;
1455 WMPropList *WMCreatePropListFromDescription(char *desc)
1457 WMPropList *plist = NULL;
1458 PLData *pldata;
1460 pldata = (PLData *) wmalloc(sizeof(PLData));
1461 memset(pldata, 0, sizeof(PLData));
1462 pldata->ptr = desc;
1463 pldata->lineNumber = 1;
1465 plist = getPropList(pldata);
1467 if (getNonSpaceChar(pldata) != 0 && plist) {
1468 COMPLAIN(pldata, _("extra data after end of property list"));
1470 * We can't just ignore garbage after the end of the description
1471 * (especially if the description was read from a file), because
1472 * the "garbage" can be the real data and the real garbage is in
1473 * fact in the beginning of the file (which is now inside plist)
1475 WMReleasePropList(plist);
1476 plist = NULL;
1479 wfree(pldata);
1481 return plist;
1484 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1486 return (indented ? indentedDescription(plist, 0) : description(plist));
1489 WMPropList *WMReadPropListFromFile(char *file)
1491 WMPropList *plist = NULL;
1492 PLData *pldata;
1493 FILE *f;
1494 struct stat stbuf;
1495 size_t length;
1497 f = fopen(file, "rb");
1498 if (!f) {
1499 /* let the user print the error message if he really needs to */
1500 /*wsyserror(_("could not open domain file '%s' for reading"), file); */
1501 return NULL;
1504 if (stat(file, &stbuf) == 0) {
1505 length = (size_t) stbuf.st_size;
1506 } else {
1507 wsyserror(_("could not get size for file '%s'"), file);
1508 fclose(f);
1509 return NULL;
1512 pldata = (PLData *) wmalloc(sizeof(PLData));
1513 memset(pldata, 0, sizeof(PLData));
1514 pldata->ptr = (char *)wmalloc(length + 1);
1515 pldata->filename = file;
1516 pldata->lineNumber = 1;
1518 if (fread(pldata->ptr, length, 1, f) != 1) {
1519 if (ferror(f)) {
1520 wsyserror(_("error reading from file '%s'"), file);
1522 plist = NULL;
1523 goto cleanup;
1526 pldata->ptr[length] = 0;
1528 plist = getPropList(pldata);
1530 if (getNonSpaceChar(pldata) != 0 && plist) {
1531 COMPLAIN(pldata, _("extra data after end of property list"));
1533 * We can't just ignore garbage after the end of the description
1534 * (especially if the description was read from a file), because
1535 * the "garbage" can be the real data and the real garbage is in
1536 * fact in the beginning of the file (which is now inside plist)
1538 WMReleasePropList(plist);
1539 plist = NULL;
1542 cleanup:
1543 wfree(pldata->ptr);
1544 wfree(pldata);
1545 fclose(f);
1547 return plist;
1550 /* TODO: review this function's code */
1552 Bool WMWritePropListToFile(WMPropList * plist, char *path, Bool atomically)
1554 char *thePath = NULL;
1555 char *desc;
1556 FILE *theFile;
1558 if (atomically) {
1559 #ifdef HAVE_MKSTEMP
1560 int fd, mask;
1561 #endif
1563 /* Use the path name of the destination file as a prefix for the
1564 * mkstemp() call so that we can be sure that both files are on
1565 * the same filesystem and the subsequent rename() will work. */
1566 thePath = wstrconcat(path, ".XXXXXX");
1568 #ifdef HAVE_MKSTEMP
1569 if ((fd = mkstemp(thePath)) < 0) {
1570 wsyserror(_("mkstemp (%s) failed"), thePath);
1571 goto failure;
1573 mask = umask(0);
1574 umask(mask);
1575 fchmod(fd, 0644 & ~mask);
1576 if ((theFile = fdopen(fd, "wb")) == NULL) {
1577 close(fd);
1579 #else
1580 if (mktemp(thePath) == NULL) {
1581 wsyserror(_("mktemp (%s) failed"), thePath);
1582 goto failure;
1584 theFile = fopen(thePath, "wb");
1585 #endif
1586 } else {
1587 thePath = wstrdup(path);
1588 theFile = fopen(thePath, "wb");
1591 if (theFile == NULL) {
1592 wsyserror(_("open (%s) failed"), thePath);
1593 goto failure;
1596 desc = indentedDescription(plist, 0);
1598 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1599 wsyserror(_("writing to file: %s failed"), thePath);
1600 wfree(desc);
1601 goto failure;
1604 wfree(desc);
1606 if (fclose(theFile) != 0) {
1607 wsyserror(_("fclose (%s) failed"), thePath);
1608 goto failure;
1611 /* If we used a temporary file, we still need to rename() it be the
1612 * real file. Also, we need to try to retain the file attributes of
1613 * the original file we are overwriting (if we are) */
1614 if (atomically) {
1615 if (rename(thePath, path) != 0) {
1616 wsyserror(_("rename ('%s' to '%s') failed"), thePath, path);
1617 goto failure;
1621 wfree(thePath);
1622 return True;
1624 failure:
1625 if (atomically) {
1626 unlink(thePath);
1627 wfree(thePath);
1630 return False;