3 (_XOPEN_SOURCE && _XOPEN_SOURCE < 500) || \
5 #define _XOPEN_SOURCE 500 /* nftw */
25 WPLString
= 0x57504c01,
27 WPLArray
= 0x57504c03,
28 WPLDictionary
= 0x57504c04
31 typedef struct W_PropList
{
44 typedef struct PLData
{
51 typedef struct StringBuffer
{
56 static unsigned hashPropList(WMPropList
* plist
);
57 static WMPropList
*getPLString(PLData
* pldata
);
58 static WMPropList
*getPLQString(PLData
* pldata
);
59 static WMPropList
*getPLData(PLData
* pldata
);
60 static WMPropList
*getPLArray(PLData
* pldata
);
61 static WMPropList
*getPLDictionary(PLData
* pldata
);
62 static WMPropList
*getPropList(PLData
* pldata
);
64 typedef unsigned (*hashFunc
) (const void *);
65 typedef Bool(*isEqualFunc
) (const void *, const void *);
66 typedef void *(*retainFunc
) (const void *);
67 typedef void (*releaseFunc
) (const void *);
69 static const WMHashTableCallbacks WMPropListHashCallbacks
= {
70 (hashFunc
) hashPropList
,
71 (isEqualFunc
) WMIsPropListEqualTo
,
76 static Bool caseSensitive
= True
;
78 #define BUFFERSIZE 8192
79 #define BUFFERSIZE_INCREMENT 1024
82 # define DPUT(s) puts(s)
87 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
88 (pld)->filename ? "file" : "PropList",\
89 (pld)->filename ? (pld)->filename : "description",\
90 (pld)->lineNumber, msg)
92 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
95 #define CHECK_BUFFER_SIZE(buf, ptr) \
96 if ((ptr) >= (buf).size-1) {\
97 (buf).size += BUFFERSIZE_INCREMENT;\
98 (buf).str = wrealloc((buf).str, (buf).size);\
101 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
102 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
103 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
104 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
105 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
106 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
107 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
109 #define MaxHashLength 64
111 static unsigned hashPropList(WMPropList
* plist
)
118 switch (plist
->type
) {
120 key
= plist
->d
.string
;
121 len
= WMIN(strlen(key
), MaxHashLength
);
122 for (i
= 0; i
< len
; i
++) {
123 ret
^= tolower(key
[i
]) << ctr
;
124 ctr
= (ctr
+ 1) % sizeof(char *);
127 ret ^= tolower(*key++) << ctr;
128 ctr = (ctr + 1) % sizeof (char *);
133 key
= WMDataBytes(plist
->d
.data
);
134 len
= WMIN(WMGetDataLength(plist
->d
.data
), MaxHashLength
);
135 for (i
= 0; i
< len
; i
++) {
136 ret
^= key
[i
] << ctr
;
137 ctr
= (ctr
+ 1) % sizeof(char *);
142 wwarning(_("Only string or data is supported for a proplist dictionary key"));
150 static WMPropList
*retainPropListByCount(WMPropList
* plist
, int count
)
152 WMPropList
*key
, *value
;
156 plist
->retainCount
+= count
;
158 switch (plist
->type
) {
163 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
164 retainPropListByCount(WMGetFromArray(plist
->d
.array
, i
), count
);
168 e
= WMEnumerateHashTable(plist
->d
.dict
);
169 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&value
, (void **)&key
)) {
170 retainPropListByCount(key
, count
);
171 retainPropListByCount(value
, count
);
175 wwarning(_("Used proplist functions on non-WMPropLists objects"));
176 wassertrv(False
, NULL
);
183 static void releasePropListByCount(WMPropList
* plist
, int count
)
185 WMPropList
*key
, *value
;
189 plist
->retainCount
-= count
;
191 switch (plist
->type
) {
193 if (plist
->retainCount
< 1) {
194 wfree(plist
->d
.string
);
199 if (plist
->retainCount
< 1) {
200 WMReleaseData(plist
->d
.data
);
205 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
206 releasePropListByCount(WMGetFromArray(plist
->d
.array
, i
), count
);
208 if (plist
->retainCount
< 1) {
209 WMFreeArray(plist
->d
.array
);
214 e
= WMEnumerateHashTable(plist
->d
.dict
);
215 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&value
, (void **)&key
)) {
216 releasePropListByCount(key
, count
);
217 releasePropListByCount(value
, count
);
219 if (plist
->retainCount
< 1) {
220 WMFreeHashTable(plist
->d
.dict
);
225 wwarning(_("Used proplist functions on non-WMPropLists objects"));
231 static char *dataDescription(WMPropList
* plist
)
233 const unsigned char *data
;
237 data
= WMDataBytes(plist
->d
.data
);
238 length
= WMGetDataLength(plist
->d
.data
);
240 retVal
= (char *)wmalloc(2 * length
+ length
/ 4 + 3);
243 for (i
= 0, j
= 1; i
< length
; i
++) {
244 retVal
[j
++] = num2char((data
[i
] >> 4) & 0x0f);
245 retVal
[j
++] = num2char(data
[i
] & 0x0f);
246 if ((i
& 0x03) == 3 && i
!= length
- 1) {
247 /* if we've just finished a 32-bit int, add a space */
257 static char *stringDescription(WMPropList
* plist
)
260 char *retVal
, *sPtr
, *dPtr
;
264 str
= plist
->d
.string
;
266 if (strlen(str
) == 0) {
267 return wstrdup("\"\"");
270 /* FIXME: make this work with unichars. */
275 while ((ch
= *sPtr
)) {
290 retVal
= (char *)wmalloc(len
+ 1);
298 while ((ch
= *sPtr
)) {
321 *dPtr
= ch
; /* " or \ */
323 } else if (numesc(ch
)) {
325 *(dPtr
++) = '0' + ((ch
>> 6) & 07);
326 *(dPtr
++) = '0' + ((ch
>> 3) & 07);
327 *dPtr
= '0' + (ch
& 07);
343 static char *description(WMPropList
* plist
)
345 WMPropList
*key
, *val
;
347 char *str
, *tmp
, *skey
, *sval
;
351 switch (plist
->type
) {
353 retstr
= stringDescription(plist
);
356 retstr
= dataDescription(plist
);
359 retstr
= wstrdup("(");
360 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
361 str
= description(WMGetFromArray(plist
->d
.array
, i
));
363 retstr
= wstrappend(retstr
, str
);
365 tmp
= (char *)wmalloc(strlen(retstr
) + strlen(str
) + 3);
366 sprintf(tmp
, "%s, %s", retstr
, str
);
372 retstr
= wstrappend(retstr
, ")");
375 retstr
= wstrdup("{");
376 e
= WMEnumerateHashTable(plist
->d
.dict
);
377 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&val
, (void **)&key
)) {
378 skey
= description(key
);
379 sval
= description(val
);
380 tmp
= (char *)wmalloc(strlen(retstr
) + strlen(skey
) + strlen(sval
) + 5);
381 sprintf(tmp
, "%s%s = %s;", retstr
, skey
, sval
);
387 retstr
= wstrappend(retstr
, "}");
390 wwarning(_("Used proplist functions on non-WMPropLists objects"));
391 wassertrv(False
, NULL
);
398 static char *indentedDescription(WMPropList
* plist
, int level
)
400 WMPropList
*key
, *val
;
402 char *str
, *tmp
, *skey
, *sval
;
406 if (plist
->type
== WPLArray
/* || plist->type==WPLDictionary */ ) {
407 retstr
= description(plist
);
409 if (retstr
&& ((2 * (level
+ 1) + strlen(retstr
)) <= 77)) {
417 switch (plist
->type
) {
419 retstr
= stringDescription(plist
);
422 retstr
= dataDescription(plist
);
425 retstr
= wstrdup("(\n");
426 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
427 str
= indentedDescription(WMGetFromArray(plist
->d
.array
, i
), level
+ 1);
429 tmp
= (char *)wmalloc(2 * (level
+ 1) + strlen(retstr
) + strlen(str
) + 1);
430 sprintf(tmp
, "%s%*s%s", retstr
, 2 * (level
+ 1), "", str
);
434 tmp
= (char *)wmalloc(2 * (level
+ 1) + strlen(retstr
) + strlen(str
) + 3);
435 sprintf(tmp
, "%s,\n%*s%s", retstr
, 2 * (level
+ 1), "", str
);
441 tmp
= (char *)wmalloc(strlen(retstr
) + 2 * level
+ 3);
442 sprintf(tmp
, "%s\n%*s)", retstr
, 2 * level
, "");
447 retstr
= wstrdup("{\n");
448 e
= WMEnumerateHashTable(plist
->d
.dict
);
449 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&val
, (void **)&key
)) {
450 skey
= indentedDescription(key
, level
+ 1);
451 sval
= indentedDescription(val
, level
+ 1);
452 tmp
= (char *)wmalloc(2 * (level
+ 1) + strlen(retstr
) + strlen(skey
)
454 sprintf(tmp
, "%s%*s%s = %s;\n", retstr
, 2 * (level
+ 1), "", skey
, sval
);
460 tmp
= (char *)wmalloc(strlen(retstr
) + 2 * level
+ 2);
461 sprintf(tmp
, "%s%*s}", retstr
, 2 * level
, "");
466 wwarning(_("Used proplist functions on non-WMPropLists objects"));
467 wassertrv(False
, NULL
);
474 static INLINE
int getChar(PLData
* pldata
)
478 c
= pldata
->ptr
[pldata
->pos
];
486 pldata
->lineNumber
++;
491 static INLINE
int getNonSpaceChar(PLData
* pldata
)
496 c
= pldata
->ptr
[pldata
->pos
];
502 pldata
->lineNumber
++;
503 } else if (!isspace(c
)) {
511 static char *unescapestr(char *src
)
513 char *dest
= wmalloc(strlen(src
) + 1);
517 for (sPtr
= src
, dPtr
= dest
; *sPtr
; sPtr
++, dPtr
++) {
522 if ((ch
>= '0') && (ch
<= '3')) {
523 /* assume next 2 chars are octal too */
524 *dPtr
= ((ch
& 07) << 6);
525 *dPtr
|= ((*(++sPtr
) & 07) << 3);
526 *dPtr
|= *(++sPtr
) & 07;
562 static WMPropList
*getPLString(PLData
* pldata
)
569 sBuf
.str
= wmalloc(BUFFERSIZE
);
570 sBuf
.size
= BUFFERSIZE
;
574 if (ISSTRINGABLE(c
)) {
575 CHECK_BUFFER_SIZE(sBuf
, ptr
);
590 char *tmp
= unescapestr(sBuf
.str
);
591 plist
= WMCreatePLString(tmp
);
600 static WMPropList
*getPLQString(PLData
* pldata
)
603 int ptr
= 0, escaping
= 0, ok
= 1;
607 sBuf
.str
= wmalloc(BUFFERSIZE
);
608 sBuf
.size
= BUFFERSIZE
;
616 } else if (c
== '"') {
620 CHECK_BUFFER_SIZE(sBuf
, ptr
);
621 sBuf
.str
[ptr
++] = '\\';
626 COMPLAIN(pldata
, _("unterminated PropList string"));
630 CHECK_BUFFER_SIZE(sBuf
, ptr
);
640 char *tmp
= unescapestr(sBuf
.str
);
641 plist
= WMCreatePLString(tmp
);
650 static WMPropList
*getPLData(PLData
* pldata
)
655 unsigned char buf
[BUFFERSIZE
], byte
;
659 data
= WMCreateDataWithCapacity(0);
662 c1
= getNonSpaceChar(pldata
);
664 COMPLAIN(pldata
, _("unterminated PropList data"));
667 } else if (c1
== '>') {
669 } else if (ishexdigit(c1
)) {
670 c2
= getNonSpaceChar(pldata
);
671 if (c2
== 0 || c2
== '>') {
672 COMPLAIN(pldata
, _("unterminated PropList data (missing hexdigit)"));
675 } else if (ishexdigit(c2
)) {
676 byte
= char2num(c1
) << 4;
677 byte
|= char2num(c2
);
679 if (len
== sizeof(buf
)) {
680 WMAppendDataBytes(data
, buf
, len
);
684 COMPLAIN(pldata
, _("non hexdigit character in PropList data"));
689 COMPLAIN(pldata
, _("non hexdigit character in PropList data"));
701 WMAppendDataBytes(data
, buf
, len
);
703 plist
= WMCreatePLData(data
);
709 static WMPropList
*getPLArray(PLData
* pldata
)
714 WMPropList
*array
, *obj
;
716 array
= WMCreatePLArray(NULL
);
719 c
= getNonSpaceChar(pldata
);
721 COMPLAIN(pldata
, _("unterminated PropList array"));
724 } else if (c
== ')') {
726 } else if (c
== ',') {
727 /* continue normally */
729 COMPLAIN(pldata
, _("missing or unterminated PropList array"));
737 obj
= getPropList(pldata
);
739 COMPLAIN(pldata
, _("could not get PropList array element"));
743 WMAddToPLArray(array
, obj
);
744 WMReleasePropList(obj
);
748 WMReleasePropList(array
);
755 static WMPropList
*getPLDictionary(PLData
* pldata
)
759 WMPropList
*dict
, *key
, *value
;
761 dict
= WMCreatePLDictionary(NULL
, NULL
);
764 c
= getNonSpaceChar(pldata
);
766 COMPLAIN(pldata
, _("unterminated PropList dictionary"));
769 } else if (c
== '}') {
773 DPUT("getting PropList dictionary key");
775 key
= getPLData(pldata
);
776 } else if (c
== '"') {
777 key
= getPLQString(pldata
);
778 } else if (ISSTRINGABLE(c
)) {
780 key
= getPLString(pldata
);
783 COMPLAIN(pldata
, _("missing PropList dictionary key"));
785 COMPLAIN(pldata
, _("missing PropList dictionary entry key "
786 "or unterminated dictionary"));
793 COMPLAIN(pldata
, _("error parsing PropList dictionary key"));
798 c
= getNonSpaceChar(pldata
);
800 WMReleasePropList(key
);
801 COMPLAIN(pldata
, _("missing = in PropList dictionary entry"));
806 DPUT("getting PropList dictionary entry value for key");
807 value
= getPropList(pldata
);
809 COMPLAIN(pldata
, _("error parsing PropList dictionary entry value"));
810 WMReleasePropList(key
);
815 c
= getNonSpaceChar(pldata
);
817 COMPLAIN(pldata
, _("missing ; in PropList dictionary entry"));
818 WMReleasePropList(key
);
819 WMReleasePropList(value
);
824 WMPutInPLDictionary(dict
, key
, value
);
825 WMReleasePropList(key
);
826 WMReleasePropList(value
);
830 WMReleasePropList(dict
);
837 static WMPropList
*getPropList(PLData
* pldata
)
842 c
= getNonSpaceChar(pldata
);
846 DPUT("End of PropList");
851 DPUT("Getting PropList dictionary");
852 plist
= getPLDictionary(pldata
);
856 DPUT("Getting PropList array");
857 plist
= getPLArray(pldata
);
861 DPUT("Getting PropList data");
862 plist
= getPLData(pldata
);
866 DPUT("Getting PropList quoted string");
867 plist
= getPLQString(pldata
);
871 if (ISSTRINGABLE(c
)) {
872 DPUT("Getting PropList string");
874 plist
= getPLString(pldata
);
876 COMPLAIN(pldata
, _("was expecting a string, data, array or "
877 "dictionary. If it's a string, try enclosing " "it with \"."));
878 if (c
== '#' || c
== '/') {
879 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
889 void WMPLSetCaseSensitive(Bool caseSensitiveness
)
891 caseSensitive
= caseSensitiveness
;
894 WMPropList
*WMCreatePLString(char *str
)
898 wassertrv(str
!= NULL
, NULL
);
900 plist
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
901 plist
->type
= WPLString
;
902 plist
->d
.string
= wstrdup(str
);
903 plist
->retainCount
= 1;
908 WMPropList
*WMCreatePLData(WMData
* data
)
912 wassertrv(data
!= NULL
, NULL
);
914 plist
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
915 plist
->type
= WPLData
;
916 plist
->d
.data
= WMRetainData(data
);
917 plist
->retainCount
= 1;
922 WMPropList
*WMCreatePLDataWithBytes(unsigned char *bytes
, unsigned int length
)
926 wassertrv(bytes
!= NULL
, NULL
);
928 plist
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
929 plist
->type
= WPLData
;
930 plist
->d
.data
= WMCreateDataWithBytes(bytes
, length
);
931 plist
->retainCount
= 1;
936 WMPropList
*WMCreatePLDataWithBytesNoCopy(unsigned char *bytes
, unsigned int length
, WMFreeDataProc
* destructor
)
940 wassertrv(bytes
!= NULL
, NULL
);
942 plist
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
943 plist
->type
= WPLData
;
944 plist
->d
.data
= WMCreateDataWithBytesNoCopy(bytes
, length
, destructor
);
945 plist
->retainCount
= 1;
950 WMPropList
*WMCreatePLArray(WMPropList
* elem
, ...)
952 WMPropList
*plist
, *nelem
;
955 plist
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
956 plist
->type
= WPLArray
;
957 plist
->d
.array
= WMCreateArray(4);
958 plist
->retainCount
= 1;
963 WMAddToArray(plist
->d
.array
, WMRetainPropList(elem
));
968 nelem
= va_arg(ap
, WMPropList
*);
973 WMAddToArray(plist
->d
.array
, WMRetainPropList(nelem
));
977 WMPropList
*WMCreatePLDictionary(WMPropList
* key
, WMPropList
* value
, ...)
979 WMPropList
*plist
, *nkey
, *nvalue
, *k
, *v
;
982 plist
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
983 plist
->type
= WPLDictionary
;
984 plist
->d
.dict
= WMCreateHashTable(WMPropListHashCallbacks
);
985 plist
->retainCount
= 1;
990 WMHashInsert(plist
->d
.dict
, WMRetainPropList(key
), WMRetainPropList(value
));
995 nkey
= va_arg(ap
, WMPropList
*);
1000 nvalue
= va_arg(ap
, WMPropList
*);
1005 if (WMHashGetItemAndKey(plist
->d
.dict
, nkey
, (void **)&v
, (void **)&k
)) {
1006 WMHashRemove(plist
->d
.dict
, k
);
1007 WMReleasePropList(k
);
1008 WMReleasePropList(v
);
1010 WMHashInsert(plist
->d
.dict
, WMRetainPropList(nkey
), WMRetainPropList(nvalue
));
1014 WMPropList
*WMRetainPropList(WMPropList
* plist
)
1016 WMPropList
*key
, *value
;
1020 plist
->retainCount
++;
1022 switch (plist
->type
) {
1027 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
1028 WMRetainPropList(WMGetFromArray(plist
->d
.array
, i
));
1032 e
= WMEnumerateHashTable(plist
->d
.dict
);
1033 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&value
, (void **)&key
)) {
1034 WMRetainPropList(key
);
1035 WMRetainPropList(value
);
1039 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1040 wassertrv(False
, NULL
);
1047 void WMReleasePropList(WMPropList
* plist
)
1049 WMPropList
*key
, *value
;
1053 plist
->retainCount
--;
1055 switch (plist
->type
) {
1057 if (plist
->retainCount
< 1) {
1058 wfree(plist
->d
.string
);
1063 if (plist
->retainCount
< 1) {
1064 WMReleaseData(plist
->d
.data
);
1069 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
1070 WMReleasePropList(WMGetFromArray(plist
->d
.array
, i
));
1072 if (plist
->retainCount
< 1) {
1073 WMFreeArray(plist
->d
.array
);
1078 e
= WMEnumerateHashTable(plist
->d
.dict
);
1079 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&value
, (void **)&key
)) {
1080 WMReleasePropList(key
);
1081 WMReleasePropList(value
);
1083 if (plist
->retainCount
< 1) {
1084 WMFreeHashTable(plist
->d
.dict
);
1089 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1095 void WMInsertInPLArray(WMPropList
* plist
, int index
, WMPropList
* item
)
1097 wassertr(plist
->type
== WPLArray
);
1099 retainPropListByCount(item
, plist
->retainCount
);
1100 WMInsertInArray(plist
->d
.array
, index
, item
);
1103 void WMAddToPLArray(WMPropList
* plist
, WMPropList
* item
)
1105 wassertr(plist
->type
== WPLArray
);
1107 retainPropListByCount(item
, plist
->retainCount
);
1108 WMAddToArray(plist
->d
.array
, item
);
1111 void WMDeleteFromPLArray(WMPropList
* plist
, int index
)
1115 wassertr(plist
->type
== WPLArray
);
1117 item
= WMGetFromArray(plist
->d
.array
, index
);
1119 WMDeleteFromArray(plist
->d
.array
, index
);
1120 releasePropListByCount(item
, plist
->retainCount
);
1124 void WMRemoveFromPLArray(WMPropList
* plist
, WMPropList
* item
)
1129 wassertr(plist
->type
== WPLArray
);
1131 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
1132 iPtr
= WMGetFromArray(plist
->d
.array
, i
);
1133 if (WMIsPropListEqualTo(item
, iPtr
)) {
1134 WMDeleteFromArray(plist
->d
.array
, i
);
1135 releasePropListByCount(iPtr
, plist
->retainCount
);
1141 void WMPutInPLDictionary(WMPropList
* plist
, WMPropList
* key
, WMPropList
* value
)
1143 wassertr(plist
->type
== WPLDictionary
);
1145 /*WMRetainPropList(key); */
1146 WMRemoveFromPLDictionary(plist
, key
);
1147 retainPropListByCount(key
, plist
->retainCount
);
1148 retainPropListByCount(value
, plist
->retainCount
);
1149 WMHashInsert(plist
->d
.dict
, key
, value
);
1150 /*WMReleasePropList(key); */
1153 void WMRemoveFromPLDictionary(WMPropList
* plist
, WMPropList
* key
)
1157 wassertr(plist
->type
== WPLDictionary
);
1159 if (WMHashGetItemAndKey(plist
->d
.dict
, key
, (void **)&v
, (void **)&k
)) {
1160 WMHashRemove(plist
->d
.dict
, k
);
1161 releasePropListByCount(k
, plist
->retainCount
);
1162 releasePropListByCount(v
, plist
->retainCount
);
1166 WMPropList
*WMMergePLDictionaries(WMPropList
* dest
, WMPropList
* source
, Bool recursive
)
1168 WMPropList
*key
, *value
, *dvalue
;
1171 wassertr(source
->type
== WPLDictionary
&& dest
->type
== WPLDictionary
);
1176 e
= WMEnumerateHashTable(source
->d
.dict
);
1177 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&value
, (void **)&key
)) {
1178 if (recursive
&& value
->type
== WPLDictionary
) {
1179 dvalue
= WMHashGet(dest
->d
.dict
, key
);
1180 if (dvalue
&& dvalue
->type
== WPLDictionary
) {
1181 WMMergePLDictionaries(dvalue
, value
, True
);
1183 WMPutInPLDictionary(dest
, key
, value
);
1186 WMPutInPLDictionary(dest
, key
, value
);
1193 WMPropList
*WMSubtractPLDictionaries(WMPropList
* dest
, WMPropList
* source
, Bool recursive
)
1195 WMPropList
*key
, *value
, *dvalue
;
1198 wassertr(source
->type
== WPLDictionary
&& dest
->type
== WPLDictionary
);
1200 if (source
== dest
) {
1201 WMPropList
*keys
= WMGetPLDictionaryKeys(dest
);
1204 for (i
= 0; i
< WMGetArrayItemCount(keys
->d
.array
); i
++) {
1205 WMRemoveFromPLDictionary(dest
, WMGetFromArray(keys
->d
.array
, i
));
1210 e
= WMEnumerateHashTable(source
->d
.dict
);
1211 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&value
, (void **)&key
)) {
1212 dvalue
= WMHashGet(dest
->d
.dict
, key
);
1215 if (WMIsPropListEqualTo(value
, dvalue
)) {
1216 WMRemoveFromPLDictionary(dest
, key
);
1217 } else if (recursive
&& value
->type
== WPLDictionary
&& dvalue
->type
== WPLDictionary
) {
1218 WMSubtractPLDictionaries(dvalue
, value
, True
);
1225 int WMGetPropListItemCount(WMPropList
* plist
)
1227 switch (plist
->type
) {
1230 return 0; /* should this be 1 instead? */
1232 return WMGetArrayItemCount(plist
->d
.array
);
1234 return (int)WMCountHashTable(plist
->d
.dict
);
1236 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1237 wassertrv(False
, 0);
1244 Bool
WMIsPLString(WMPropList
* plist
)
1246 return (plist
->type
== WPLString
);
1249 Bool
WMIsPLData(WMPropList
* plist
)
1251 return (plist
->type
== WPLData
);
1254 Bool
WMIsPLArray(WMPropList
* plist
)
1256 return (plist
->type
== WPLArray
);
1259 Bool
WMIsPLDictionary(WMPropList
* plist
)
1261 return (plist
->type
== WPLDictionary
);
1264 Bool
WMIsPropListEqualTo(WMPropList
* plist
, WMPropList
* other
)
1266 WMPropList
*key1
, *item1
, *item2
;
1267 WMHashEnumerator enumerator
;
1270 if (plist
->type
!= other
->type
)
1273 switch (plist
->type
) {
1275 if (caseSensitive
) {
1276 return (strcmp(plist
->d
.string
, other
->d
.string
) == 0);
1278 return (strcasecmp(plist
->d
.string
, other
->d
.string
) == 0);
1281 return WMIsDataEqualToData(plist
->d
.data
, other
->d
.data
);
1283 n
= WMGetArrayItemCount(plist
->d
.array
);
1284 if (n
!= WMGetArrayItemCount(other
->d
.array
))
1286 for (i
= 0; i
< n
; i
++) {
1287 item1
= WMGetFromArray(plist
->d
.array
, i
);
1288 item2
= WMGetFromArray(other
->d
.array
, i
);
1289 if (!WMIsPropListEqualTo(item1
, item2
))
1294 if (WMCountHashTable(plist
->d
.dict
) != WMCountHashTable(other
->d
.dict
))
1296 enumerator
= WMEnumerateHashTable(plist
->d
.dict
);
1297 while (WMNextHashEnumeratorItemAndKey(&enumerator
, (void **)&item1
, (void **)&key1
)) {
1298 item2
= WMHashGet(other
->d
.dict
, key1
);
1299 if (!item2
|| !item1
|| !WMIsPropListEqualTo(item1
, item2
))
1304 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1305 wassertrv(False
, False
);
1312 char *WMGetFromPLString(WMPropList
* plist
)
1314 wassertrv(plist
->type
== WPLString
, NULL
);
1316 return plist
->d
.string
;
1319 WMData
*WMGetFromPLData(WMPropList
* plist
)
1321 wassertrv(plist
->type
== WPLData
, NULL
);
1323 return plist
->d
.data
;
1326 const unsigned char *WMGetPLDataBytes(WMPropList
* plist
)
1328 wassertrv(plist
->type
== WPLData
, NULL
);
1330 return WMDataBytes(plist
->d
.data
);
1333 int WMGetPLDataLength(WMPropList
* plist
)
1335 wassertrv(plist
->type
== WPLData
, 0);
1337 return WMGetDataLength(plist
->d
.data
);
1340 WMPropList
*WMGetFromPLArray(WMPropList
* plist
, int index
)
1342 wassertrv(plist
->type
== WPLArray
, NULL
);
1344 return WMGetFromArray(plist
->d
.array
, index
);
1347 WMPropList
*WMGetFromPLDictionary(WMPropList
* plist
, WMPropList
* key
)
1349 wassertrv(plist
->type
== WPLDictionary
, NULL
);
1351 return WMHashGet(plist
->d
.dict
, key
);
1354 WMPropList
*WMGetPLDictionaryKeys(WMPropList
* plist
)
1356 WMPropList
*array
, *key
;
1357 WMHashEnumerator enumerator
;
1359 wassertrv(plist
->type
== WPLDictionary
, NULL
);
1361 array
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
1362 array
->type
= WPLArray
;
1363 array
->d
.array
= WMCreateArray(WMCountHashTable(plist
->d
.dict
));
1364 array
->retainCount
= 1;
1366 enumerator
= WMEnumerateHashTable(plist
->d
.dict
);
1367 while ((key
= WMNextHashEnumeratorKey(&enumerator
))) {
1368 WMAddToArray(array
->d
.array
, WMRetainPropList(key
));
1374 WMPropList
*WMShallowCopyPropList(WMPropList
* plist
)
1376 WMPropList
*ret
= NULL
;
1377 WMPropList
*key
, *item
;
1382 switch (plist
->type
) {
1384 ret
= WMCreatePLString(plist
->d
.string
);
1387 data
= WMCreateDataWithData(plist
->d
.data
);
1388 ret
= WMCreatePLData(data
);
1389 WMReleaseData(data
);
1392 ret
= (WMPropList
*) wmalloc(sizeof(W_PropList
));
1393 ret
->type
= WPLArray
;
1394 ret
->d
.array
= WMCreateArrayWithArray(plist
->d
.array
);
1395 ret
->retainCount
= 1;
1397 for (i
= 0; i
< WMGetArrayItemCount(ret
->d
.array
); i
++)
1398 WMRetainPropList(WMGetFromArray(ret
->d
.array
, i
));
1402 ret
= WMCreatePLDictionary(NULL
, NULL
);
1403 e
= WMEnumerateHashTable(plist
->d
.dict
);
1404 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&item
, (void **)&key
)) {
1405 WMPutInPLDictionary(ret
, key
, item
);
1409 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1410 wassertrv(False
, NULL
);
1417 WMPropList
*WMDeepCopyPropList(WMPropList
* plist
)
1419 WMPropList
*ret
= NULL
;
1420 WMPropList
*key
, *item
;
1425 switch (plist
->type
) {
1427 ret
= WMCreatePLString(plist
->d
.string
);
1430 data
= WMCreateDataWithData(plist
->d
.data
);
1431 ret
= WMCreatePLData(data
);
1432 WMReleaseData(data
);
1435 ret
= WMCreatePLArray(NULL
);
1436 for (i
= 0; i
< WMGetArrayItemCount(plist
->d
.array
); i
++) {
1437 item
= WMDeepCopyPropList(WMGetFromArray(plist
->d
.array
, i
));
1438 WMAddToArray(ret
->d
.array
, item
);
1442 ret
= WMCreatePLDictionary(NULL
, NULL
);
1443 e
= WMEnumerateHashTable(plist
->d
.dict
);
1444 /* While we copy an existing dictionary there is no way that we can
1445 * have duplicate keys, so we don't need to first remove a key/value
1446 * pair before inserting the new key/value.
1448 while (WMNextHashEnumeratorItemAndKey(&e
, (void **)&item
, (void **)&key
)) {
1449 WMHashInsert(ret
->d
.dict
, WMDeepCopyPropList(key
), WMDeepCopyPropList(item
));
1453 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1454 wassertrv(False
, NULL
);
1461 WMPropList
*WMCreatePropListFromDescription(char *desc
)
1463 WMPropList
*plist
= NULL
;
1466 pldata
= (PLData
*) wmalloc(sizeof(PLData
));
1467 memset(pldata
, 0, sizeof(PLData
));
1469 pldata
->lineNumber
= 1;
1471 plist
= getPropList(pldata
);
1473 if (getNonSpaceChar(pldata
) != 0 && plist
) {
1474 COMPLAIN(pldata
, _("extra data after end of property list"));
1476 * We can't just ignore garbage after the end of the description
1477 * (especially if the description was read from a file), because
1478 * the "garbage" can be the real data and the real garbage is in
1479 * fact in the beginning of the file (which is now inside plist)
1481 WMReleasePropList(plist
);
1490 char *WMGetPropListDescription(WMPropList
* plist
, Bool indented
)
1492 return (indented
? indentedDescription(plist
, 0) : description(plist
));
1495 WMPropList
*WMReadPropListFromFile(char *file
)
1497 WMPropList
*plist
= NULL
;
1503 f
= fopen(file
, "rb");
1505 /* let the user print the error message if he really needs to */
1506 /*werror(_("could not open domain file '%s' for reading"), file); */
1510 if (stat(file
, &stbuf
) == 0) {
1511 length
= (size_t) stbuf
.st_size
;
1513 werror(_("could not get size for file '%s'"), file
);
1518 pldata
= (PLData
*) wmalloc(sizeof(PLData
));
1519 memset(pldata
, 0, sizeof(PLData
));
1520 pldata
->ptr
= (char *)wmalloc(length
+ 1);
1521 pldata
->filename
= file
;
1522 pldata
->lineNumber
= 1;
1524 if (fread(pldata
->ptr
, length
, 1, f
) != 1) {
1526 werror(_("error reading from file '%s'"), file
);
1532 pldata
->ptr
[length
] = 0;
1534 plist
= getPropList(pldata
);
1536 if (getNonSpaceChar(pldata
) != 0 && plist
) {
1537 COMPLAIN(pldata
, _("extra data after end of property list"));
1539 * We can't just ignore garbage after the end of the description
1540 * (especially if the description was read from a file), because
1541 * the "garbage" can be the real data and the real garbage is in
1542 * fact in the beginning of the file (which is now inside plist)
1544 WMReleasePropList(plist
);
1556 /* TODO: review this function's code */
1558 Bool
WMWritePropListToFile(WMPropList
* plist
, char *path
)
1560 char *thePath
= NULL
;
1567 if (!wmkdirhier(path
))
1570 /* Use the path name of the destination file as a prefix for the
1571 * mkstemp() call so that we can be sure that both files are on
1572 * the same filesystem and the subsequent rename() will work. */
1573 thePath
= wstrconcat(path
, ".XXXXXX");
1576 if ((fd
= mkstemp(thePath
)) < 0) {
1577 werror(_("mkstemp (%s) failed"), thePath
);
1582 fchmod(fd
, 0644 & ~mask
);
1583 if ((theFile
= fdopen(fd
, "wb")) == NULL
) {
1587 if (mktemp(thePath
) == NULL
) {
1588 werror(_("mktemp (%s) failed"), thePath
);
1591 theFile
= fopen(thePath
, "wb");
1594 if (theFile
== NULL
) {
1595 werror(_("open (%s) failed"), thePath
);
1599 desc
= indentedDescription(plist
, 0);
1601 if (fprintf(theFile
, "%s\n", desc
) != strlen(desc
) + 1) {
1602 werror(_("writing to file: %s failed"), thePath
);
1609 (void)fsync(fileno(theFile
));
1610 if (fclose(theFile
) != 0) {
1611 werror(_("fclose (%s) failed"), thePath
);
1615 /* If we used a temporary file, we still need to rename() it be the
1616 * real file. Also, we need to try to retain the file attributes of
1617 * the original file we are overwriting (if we are) */
1618 if (rename(thePath
, path
) != 0) {
1619 werror(_("rename ('%s' to '%s') failed"), thePath
, path
);
1633 * create a directory hierarchy
1635 * if the last octet of `path' is `/', the full path is
1636 * assumed to be a directory; otherwise path is assumed to be a
1637 * file, and the last component is stripped off. the rest is the
1638 * the hierarchy to be created.
1640 * refuses to create anything outside $GNUSTEP_USER_ROOT
1642 * returns 1 on success, 0 on failure
1644 int wmkdirhier(const char *path
)
1646 char *t
, *thePath
= NULL
, buf
[1024];
1650 /* Only create directories under $GNUSTEP_USER_ROOT */
1651 if ((t
= wusergnusteppath()) == NULL
)
1653 if (strncmp(path
, t
, strlen(t
)) != 0)
1656 thePath
= wstrdup(path
);
1657 /* Strip the trailing component if it is a file */
1658 p
= strlen(thePath
);
1659 while (p
&& thePath
[p
] != '/')
1660 thePath
[p
--] = '\0';
1664 /* Shortcut if it already exists */
1665 if (stat(thePath
, &st
) == 0) {
1667 if (S_ISDIR(st
.st_mode
)) {
1668 /* Is a directory alright */
1671 /* Exists, but not a directory, the caller
1672 * might just as well abort now */
1677 memset(buf
, 0, sizeof(buf
));
1678 strncpy(buf
, t
, sizeof(buf
) - 1);
1680 plen
= strlen(thePath
);
1683 while (p
++ < plen
&& thePath
[p
] != '/')
1686 strncpy(buf
, thePath
, p
);
1687 if (mkdir(buf
, 0777) == -1 && errno
== EEXIST
&&
1688 stat(buf
, &st
) == 0 && !S_ISDIR(st
.st_mode
)) {
1689 werror(_("Could not create component %s"), buf
);
1700 static int wrmdirhier_fn(const char *path
, const struct stat
*st
,
1701 int type
, struct FTW
*ftw
)
1712 return unlink(path
);
1725 * remove a directory hierarchy
1727 * refuses to remove anything outside $GNUSTEP_USER_ROOT
1729 * returns 1 on success, 0 on failure
1731 * TODO: revisit what's error and what's not
1733 * with inspirations from OpenBSD's bin/rm/rm.c
1735 int wrmdirhier(const char *path
)
1741 /* Only remove directories under $GNUSTEP_USER_ROOT */
1742 if ((t
= wusergnusteppath()) == NULL
)
1744 if (strncmp(path
, t
, strlen(t
)) != 0)
1747 /* Shortcut if it doesn't exist to begin with */
1748 if (stat(path
, &st
) == -1)
1751 error
= nftw(path
, wrmdirhier_fn
, 1, FTW_PHYS
);