Avoid icon change to default on winspector save
[wmaker-crm.git] / WINGs / proplist.c
blob7b0f49d16723e5841a1bc75fdceb8ffbe30ac072
2 #include <sys/types.h>
3 #include <sys/stat.h>
5 #include <ctype.h>
6 #include <errno.h>
7 #include <ftw.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <strings.h>
13 #include <unistd.h>
15 #include "WUtil.h"
16 #include "wconfig.h"
18 typedef enum {
19 WPLString = 0x57504c01,
20 WPLData = 0x57504c02,
21 WPLArray = 0x57504c03,
22 WPLDictionary = 0x57504c04
23 } 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;
38 typedef struct PLData {
39 char *ptr;
40 int pos;
41 char *filename;
42 int lineNumber;
43 } PLData;
45 typedef struct StringBuffer {
46 char *str;
47 int size;
48 } StringBuffer;
50 static unsigned hashPropList(WMPropList * plist);
51 static WMPropList *getPLString(PLData * pldata);
52 static WMPropList *getPLQString(PLData * pldata);
53 static WMPropList *getPLData(PLData * pldata);
54 static WMPropList *getPLArray(PLData * pldata);
55 static WMPropList *getPLDictionary(PLData * pldata);
56 static WMPropList *getPropList(PLData * pldata);
58 typedef unsigned (*hashFunc) (const void *);
59 typedef Bool(*isEqualFunc) (const void *, const void *);
60 typedef void *(*retainFunc) (const void *);
61 typedef void (*releaseFunc) (const void *);
63 static const WMHashTableCallbacks WMPropListHashCallbacks = {
64 (hashFunc) hashPropList,
65 (isEqualFunc) WMIsPropListEqualTo,
66 (retainFunc) NULL,
67 (releaseFunc) NULL
70 static Bool caseSensitive = True;
72 #define BUFFERSIZE 8192
73 #define BUFFERSIZE_INCREMENT 1024
75 #if 0
76 # define DPUT(s) puts(s)
77 #else
78 # define DPUT(s)
79 #endif
81 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
82 (pld)->filename ? "file" : "PropList",\
83 (pld)->filename ? (pld)->filename : "description",\
84 (pld)->lineNumber, msg)
86 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
87 || (c)=='+')
89 #define CHECK_BUFFER_SIZE(buf, ptr) \
90 if ((ptr) >= (buf).size-1) {\
91 (buf).size += BUFFERSIZE_INCREMENT;\
92 (buf).str = wrealloc((buf).str, (buf).size);\
95 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
96 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
97 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
98 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
99 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
100 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
101 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
103 #define MaxHashLength 64
105 static unsigned hashPropList(WMPropList * plist)
107 unsigned ret = 0;
108 unsigned ctr = 0;
109 const char *key;
110 int i, len;
112 switch (plist->type) {
113 case WPLString:
114 key = plist->d.string;
115 len = WMIN(strlen(key), MaxHashLength);
116 for (i = 0; i < len; i++) {
117 ret ^= tolower(key[i]) << ctr;
118 ctr = (ctr + 1) % sizeof(char *);
120 /*while (*key) {
121 ret ^= tolower(*key++) << ctr;
122 ctr = (ctr + 1) % sizeof (char *);
123 } */
124 break;
126 case WPLData:
127 key = WMDataBytes(plist->d.data);
128 len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
129 for (i = 0; i < len; i++) {
130 ret ^= key[i] << ctr;
131 ctr = (ctr + 1) % sizeof(char *);
133 break;
135 default:
136 wwarning(_("Only string or data is supported for a proplist dictionary key"));
137 wassertrv(False, 0);
138 break;
141 return ret;
144 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
146 WMPropList *key, *value;
147 WMHashEnumerator e;
148 int i;
150 plist->retainCount += count;
152 switch (plist->type) {
153 case WPLString:
154 case WPLData:
155 break;
156 case WPLArray:
157 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
158 retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
160 break;
161 case WPLDictionary:
162 e = WMEnumerateHashTable(plist->d.dict);
163 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
164 retainPropListByCount(key, count);
165 retainPropListByCount(value, count);
167 break;
168 default:
169 wwarning(_("Used proplist functions on non-WMPropLists objects"));
170 wassertrv(False, NULL);
171 break;
174 return plist;
177 static void releasePropListByCount(WMPropList * plist, int count)
179 WMPropList *key, *value;
180 WMHashEnumerator e;
181 int i;
183 plist->retainCount -= count;
185 switch (plist->type) {
186 case WPLString:
187 if (plist->retainCount < 1) {
188 wfree(plist->d.string);
189 wfree(plist);
191 break;
192 case WPLData:
193 if (plist->retainCount < 1) {
194 WMReleaseData(plist->d.data);
195 wfree(plist);
197 break;
198 case WPLArray:
199 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
200 releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
202 if (plist->retainCount < 1) {
203 WMFreeArray(plist->d.array);
204 wfree(plist);
206 break;
207 case WPLDictionary:
208 e = WMEnumerateHashTable(plist->d.dict);
209 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
210 releasePropListByCount(key, count);
211 releasePropListByCount(value, count);
213 if (plist->retainCount < 1) {
214 WMFreeHashTable(plist->d.dict);
215 wfree(plist);
217 break;
218 default:
219 wwarning(_("Used proplist functions on non-WMPropLists objects"));
220 wassertr(False);
221 break;
225 static char *dataDescription(WMPropList * plist)
227 const unsigned char *data;
228 char *retVal;
229 int i, j, length;
231 data = WMDataBytes(plist->d.data);
232 length = WMGetDataLength(plist->d.data);
234 retVal = (char *)wmalloc(2 * length + length / 4 + 3);
236 retVal[0] = '<';
237 for (i = 0, j = 1; i < length; i++) {
238 retVal[j++] = num2char((data[i] >> 4) & 0x0f);
239 retVal[j++] = num2char(data[i] & 0x0f);
240 if ((i & 0x03) == 3 && i != length - 1) {
241 /* if we've just finished a 32-bit int, add a space */
242 retVal[j++] = ' ';
245 retVal[j++] = '>';
246 retVal[j] = '\0';
248 return retVal;
251 static char *stringDescription(WMPropList * plist)
253 const char *str;
254 char *retVal, *sPtr, *dPtr;
255 int len, quote;
256 unsigned char ch;
258 str = plist->d.string;
260 if (strlen(str) == 0) {
261 return wstrdup("\"\"");
264 /* FIXME: make this work with unichars. */
266 quote = 0;
267 sPtr = (char *)str;
268 len = 0;
269 while ((ch = *sPtr)) {
270 if (!noquote(ch)) {
271 quote = 1;
272 if (charesc(ch))
273 len++;
274 else if (numesc(ch))
275 len += 3;
277 sPtr++;
278 len++;
281 if (quote)
282 len += 2;
284 retVal = (char *)wmalloc(len + 1);
286 sPtr = (char *)str;
287 dPtr = retVal;
289 if (quote)
290 *dPtr++ = '"';
292 while ((ch = *sPtr)) {
293 if (charesc(ch)) {
294 *(dPtr++) = '\\';
295 switch (ch) {
296 case '\a':
297 *dPtr = 'a';
298 break;
299 case '\b':
300 *dPtr = 'b';
301 break;
302 case '\t':
303 *dPtr = 't';
304 break;
305 case '\n':
306 *dPtr = 'n';
307 break;
308 case '\v':
309 *dPtr = 'v';
310 break;
311 case '\f':
312 *dPtr = 'f';
313 break;
314 default:
315 *dPtr = ch; /* " or \ */
317 } else if (numesc(ch)) {
318 *(dPtr++) = '\\';
319 *(dPtr++) = '0' + ((ch >> 6) & 07);
320 *(dPtr++) = '0' + ((ch >> 3) & 07);
321 *dPtr = '0' + (ch & 07);
322 } else {
323 *dPtr = ch;
325 sPtr++;
326 dPtr++;
329 if (quote)
330 *dPtr++ = '"';
332 *dPtr = '\0';
334 return retVal;
337 static char *description(WMPropList * plist)
339 WMPropList *key, *val;
340 char *retstr = NULL;
341 char *str, *tmp, *skey, *sval;
342 WMHashEnumerator e;
343 int i;
345 switch (plist->type) {
346 case WPLString:
347 retstr = stringDescription(plist);
348 break;
349 case WPLData:
350 retstr = dataDescription(plist);
351 break;
352 case WPLArray:
353 retstr = wstrdup("(");
354 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
355 str = description(WMGetFromArray(plist->d.array, i));
356 if (i == 0) {
357 retstr = wstrappend(retstr, str);
358 } else {
359 tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
360 sprintf(tmp, "%s, %s", retstr, str);
361 wfree(retstr);
362 retstr = tmp;
364 wfree(str);
366 retstr = wstrappend(retstr, ")");
367 break;
368 case WPLDictionary:
369 retstr = wstrdup("{");
370 e = WMEnumerateHashTable(plist->d.dict);
371 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
372 skey = description(key);
373 sval = description(val);
374 tmp = (char *)wmalloc(strlen(retstr) + strlen(skey) + strlen(sval) + 5);
375 sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
376 wfree(skey);
377 wfree(sval);
378 wfree(retstr);
379 retstr = tmp;
381 retstr = wstrappend(retstr, "}");
382 break;
383 default:
384 wwarning(_("Used proplist functions on non-WMPropLists objects"));
385 wassertrv(False, NULL);
386 break;
389 return retstr;
392 static char *indentedDescription(WMPropList * plist, int level)
394 WMPropList *key, *val;
395 char *retstr = NULL;
396 char *str, *tmp, *skey, *sval;
397 WMHashEnumerator e;
398 int i;
400 if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
401 retstr = description(plist);
403 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
404 return retstr;
405 } else if (retstr) {
406 wfree(retstr);
407 retstr = NULL;
411 switch (plist->type) {
412 case WPLString:
413 retstr = stringDescription(plist);
414 break;
415 case WPLData:
416 retstr = dataDescription(plist);
417 break;
418 case WPLArray:
419 retstr = wstrdup("(\n");
420 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
421 str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
422 if (i == 0) {
423 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
424 sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
425 wfree(retstr);
426 retstr = tmp;
427 } else {
428 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
429 sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
430 wfree(retstr);
431 retstr = tmp;
433 wfree(str);
435 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
436 sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
437 wfree(retstr);
438 retstr = tmp;
439 break;
440 case WPLDictionary:
441 retstr = wstrdup("{\n");
442 e = WMEnumerateHashTable(plist->d.dict);
443 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
444 skey = indentedDescription(key, level + 1);
445 sval = indentedDescription(val, level + 1);
446 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(skey)
447 + strlen(sval) + 6);
448 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
449 wfree(skey);
450 wfree(sval);
451 wfree(retstr);
452 retstr = tmp;
454 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
455 sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
456 wfree(retstr);
457 retstr = tmp;
458 break;
459 default:
460 wwarning(_("Used proplist functions on non-WMPropLists objects"));
461 wassertrv(False, NULL);
462 break;
465 return retstr;
468 static INLINE int getChar(PLData * pldata)
470 int c;
472 c = pldata->ptr[pldata->pos];
473 if (c == 0) {
474 return 0;
477 pldata->pos++;
479 if (c == '\n')
480 pldata->lineNumber++;
482 return c;
485 static INLINE int getNonSpaceChar(PLData * pldata)
487 int c;
489 while (1) {
490 c = pldata->ptr[pldata->pos];
491 if (c == 0) {
492 break;
494 pldata->pos++;
495 if (c == '\n') {
496 pldata->lineNumber++;
497 } else if (!isspace(c)) {
498 break;
502 return c;
505 static char *unescapestr(char *src)
507 char *dest = wmalloc(strlen(src) + 1);
508 char *sPtr, *dPtr;
509 char ch;
511 for (sPtr = src, dPtr = dest; *sPtr; sPtr++, dPtr++) {
512 if (*sPtr != '\\') {
513 *dPtr = *sPtr;
514 } else {
515 ch = *(++sPtr);
516 if ((ch >= '0') && (ch <= '3')) {
517 /* assume next 2 chars are octal too */
518 *dPtr = ((ch & 07) << 6);
519 *dPtr |= ((*(++sPtr) & 07) << 3);
520 *dPtr |= *(++sPtr) & 07;
521 } else {
522 switch (ch) {
523 case 'a':
524 *dPtr = '\a';
525 break;
526 case 'b':
527 *dPtr = '\b';
528 break;
529 case 't':
530 *dPtr = '\t';
531 break;
532 case 'r':
533 *dPtr = '\r';
534 break;
535 case 'n':
536 *dPtr = '\n';
537 break;
538 case 'v':
539 *dPtr = '\v';
540 break;
541 case 'f':
542 *dPtr = '\f';
543 break;
544 default:
545 *dPtr = *sPtr;
551 *dPtr = 0;
553 return dest;
556 static WMPropList *getPLString(PLData * pldata)
558 WMPropList *plist;
559 StringBuffer sBuf;
560 int ptr = 0;
561 int c;
563 sBuf.str = wmalloc(BUFFERSIZE);
564 sBuf.size = BUFFERSIZE;
566 while (1) {
567 c = getChar(pldata);
568 if (ISSTRINGABLE(c)) {
569 CHECK_BUFFER_SIZE(sBuf, ptr);
570 sBuf.str[ptr++] = c;
571 } else {
572 if (c != 0) {
573 pldata->pos--;
575 break;
579 sBuf.str[ptr] = 0;
581 if (ptr == 0) {
582 plist = NULL;
583 } else {
584 char *tmp = unescapestr(sBuf.str);
585 plist = WMCreatePLString(tmp);
586 wfree(tmp);
589 wfree(sBuf.str);
591 return plist;
594 static WMPropList *getPLQString(PLData * pldata)
596 WMPropList *plist;
597 int ptr = 0, escaping = 0, ok = 1;
598 int c;
599 StringBuffer sBuf;
601 sBuf.str = wmalloc(BUFFERSIZE);
602 sBuf.size = BUFFERSIZE;
604 while (1) {
605 c = getChar(pldata);
606 if (!escaping) {
607 if (c == '\\') {
608 escaping = 1;
609 continue;
610 } else if (c == '"') {
611 break;
613 } else {
614 CHECK_BUFFER_SIZE(sBuf, ptr);
615 sBuf.str[ptr++] = '\\';
616 escaping = 0;
619 if (c == 0) {
620 COMPLAIN(pldata, _("unterminated PropList string"));
621 ok = 0;
622 break;
623 } else {
624 CHECK_BUFFER_SIZE(sBuf, ptr);
625 sBuf.str[ptr++] = c;
629 sBuf.str[ptr] = 0;
631 if (!ok) {
632 plist = NULL;
633 } else {
634 char *tmp = unescapestr(sBuf.str);
635 plist = WMCreatePLString(tmp);
636 wfree(tmp);
639 wfree(sBuf.str);
641 return plist;
644 static WMPropList *getPLData(PLData * pldata)
646 int ok = 1;
647 int len = 0;
648 int c1, c2;
649 unsigned char buf[BUFFERSIZE], byte;
650 WMPropList *plist;
651 WMData *data;
653 data = WMCreateDataWithCapacity(0);
655 while (1) {
656 c1 = getNonSpaceChar(pldata);
657 if (c1 == 0) {
658 COMPLAIN(pldata, _("unterminated PropList data"));
659 ok = 0;
660 break;
661 } else if (c1 == '>') {
662 break;
663 } else if (ishexdigit(c1)) {
664 c2 = getNonSpaceChar(pldata);
665 if (c2 == 0 || c2 == '>') {
666 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
667 ok = 0;
668 break;
669 } else if (ishexdigit(c2)) {
670 byte = char2num(c1) << 4;
671 byte |= char2num(c2);
672 buf[len++] = byte;
673 if (len == sizeof(buf)) {
674 WMAppendDataBytes(data, buf, len);
675 len = 0;
677 } else {
678 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
679 ok = 0;
680 break;
682 } else {
683 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
684 ok = 0;
685 break;
689 if (!ok) {
690 WMReleaseData(data);
691 return NULL;
694 if (len > 0)
695 WMAppendDataBytes(data, buf, len);
697 plist = WMCreatePLData(data);
698 WMReleaseData(data);
700 return plist;
703 static WMPropList *getPLArray(PLData * pldata)
705 Bool first = True;
706 int ok = 1;
707 int c;
708 WMPropList *array, *obj;
710 array = WMCreatePLArray(NULL);
712 while (1) {
713 c = getNonSpaceChar(pldata);
714 if (c == 0) {
715 COMPLAIN(pldata, _("unterminated PropList array"));
716 ok = 0;
717 break;
718 } else if (c == ')') {
719 break;
720 } else if (c == ',') {
721 /* continue normally */
722 } else if (!first) {
723 COMPLAIN(pldata, _("missing or unterminated PropList array"));
724 ok = 0;
725 break;
726 } else {
727 pldata->pos--;
729 first = False;
731 obj = getPropList(pldata);
732 if (!obj) {
733 COMPLAIN(pldata, _("could not get PropList array element"));
734 ok = 0;
735 break;
737 WMAddToPLArray(array, obj);
738 WMReleasePropList(obj);
741 if (!ok) {
742 WMReleasePropList(array);
743 array = NULL;
746 return array;
749 static WMPropList *getPLDictionary(PLData * pldata)
751 int ok = 1;
752 int c;
753 WMPropList *dict, *key, *value;
755 dict = WMCreatePLDictionary(NULL, NULL);
757 while (1) {
758 c = getNonSpaceChar(pldata);
759 if (c == 0) {
760 COMPLAIN(pldata, _("unterminated PropList dictionary"));
761 ok = 0;
762 break;
763 } else if (c == '}') {
764 break;
767 DPUT("getting PropList dictionary key");
768 if (c == '<') {
769 key = getPLData(pldata);
770 } else if (c == '"') {
771 key = getPLQString(pldata);
772 } else if (ISSTRINGABLE(c)) {
773 pldata->pos--;
774 key = getPLString(pldata);
775 } else {
776 if (c == '=') {
777 COMPLAIN(pldata, _("missing PropList dictionary key"));
778 } else {
779 COMPLAIN(pldata, _("missing PropList dictionary entry key "
780 "or unterminated dictionary"));
782 ok = 0;
783 break;
786 if (!key) {
787 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
788 ok = 0;
789 break;
792 c = getNonSpaceChar(pldata);
793 if (c != '=') {
794 WMReleasePropList(key);
795 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
796 ok = 0;
797 break;
800 DPUT("getting PropList dictionary entry value for key");
801 value = getPropList(pldata);
802 if (!value) {
803 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
804 WMReleasePropList(key);
805 ok = 0;
806 break;
809 c = getNonSpaceChar(pldata);
810 if (c != ';') {
811 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
812 WMReleasePropList(key);
813 WMReleasePropList(value);
814 ok = 0;
815 break;
818 WMPutInPLDictionary(dict, key, value);
819 WMReleasePropList(key);
820 WMReleasePropList(value);
823 if (!ok) {
824 WMReleasePropList(dict);
825 dict = NULL;
828 return dict;
831 static WMPropList *getPropList(PLData * pldata)
833 WMPropList *plist;
834 int c;
836 c = getNonSpaceChar(pldata);
838 switch (c) {
839 case 0:
840 DPUT("End of PropList");
841 plist = NULL;
842 break;
844 case '{':
845 DPUT("Getting PropList dictionary");
846 plist = getPLDictionary(pldata);
847 break;
849 case '(':
850 DPUT("Getting PropList array");
851 plist = getPLArray(pldata);
852 break;
854 case '<':
855 DPUT("Getting PropList data");
856 plist = getPLData(pldata);
857 break;
859 case '"':
860 DPUT("Getting PropList quoted string");
861 plist = getPLQString(pldata);
862 break;
864 default:
865 if (ISSTRINGABLE(c)) {
866 DPUT("Getting PropList string");
867 pldata->pos--;
868 plist = getPLString(pldata);
869 } else {
870 COMPLAIN(pldata, _("was expecting a string, data, array or "
871 "dictionary. If it's a string, try enclosing " "it with \"."));
872 if (c == '#' || c == '/') {
873 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
875 plist = NULL;
877 break;
880 return plist;
883 void WMPLSetCaseSensitive(Bool caseSensitiveness)
885 caseSensitive = caseSensitiveness;
888 WMPropList *WMCreatePLString(char *str)
890 WMPropList *plist;
892 wassertrv(str != NULL, NULL);
894 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
895 plist->type = WPLString;
896 plist->d.string = wstrdup(str);
897 plist->retainCount = 1;
899 return plist;
902 WMPropList *WMCreatePLData(WMData * data)
904 WMPropList *plist;
906 wassertrv(data != NULL, NULL);
908 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
909 plist->type = WPLData;
910 plist->d.data = WMRetainData(data);
911 plist->retainCount = 1;
913 return plist;
916 WMPropList *WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
918 WMPropList *plist;
920 wassertrv(bytes != NULL, NULL);
922 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
923 plist->type = WPLData;
924 plist->d.data = WMCreateDataWithBytes(bytes, length);
925 plist->retainCount = 1;
927 return plist;
930 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
932 WMPropList *plist;
934 wassertrv(bytes != NULL, NULL);
936 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 pldata->ptr = desc;
1462 pldata->lineNumber = 1;
1464 plist = getPropList(pldata);
1466 if (getNonSpaceChar(pldata) != 0 && plist) {
1467 COMPLAIN(pldata, _("extra data after end of property list"));
1469 * We can't just ignore garbage after the end of the description
1470 * (especially if the description was read from a file), because
1471 * the "garbage" can be the real data and the real garbage is in
1472 * fact in the beginning of the file (which is now inside plist)
1474 WMReleasePropList(plist);
1475 plist = NULL;
1478 wfree(pldata);
1480 return plist;
1483 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1485 return (indented ? indentedDescription(plist, 0) : description(plist));
1488 WMPropList *WMReadPropListFromFile(char *file)
1490 WMPropList *plist = NULL;
1491 PLData *pldata;
1492 FILE *f;
1493 struct stat stbuf;
1494 size_t length;
1496 f = fopen(file, "rb");
1497 if (!f) {
1498 /* let the user print the error message if he really needs to */
1499 /*werror(_("could not open domain file '%s' for reading"), file); */
1500 return NULL;
1503 if (stat(file, &stbuf) == 0) {
1504 length = (size_t) stbuf.st_size;
1505 } else {
1506 werror(_("could not get size for file '%s'"), file);
1507 fclose(f);
1508 return NULL;
1511 pldata = (PLData *) wmalloc(sizeof(PLData));
1512 pldata->ptr = (char *)wmalloc(length + 1);
1513 pldata->filename = file;
1514 pldata->lineNumber = 1;
1516 if (fread(pldata->ptr, length, 1, f) != 1) {
1517 if (ferror(f)) {
1518 werror(_("error reading from file '%s'"), file);
1520 plist = NULL;
1521 goto cleanup;
1524 pldata->ptr[length] = 0;
1526 plist = getPropList(pldata);
1528 if (getNonSpaceChar(pldata) != 0 && plist) {
1529 COMPLAIN(pldata, _("extra data after end of property list"));
1531 * We can't just ignore garbage after the end of the description
1532 * (especially if the description was read from a file), because
1533 * the "garbage" can be the real data and the real garbage is in
1534 * fact in the beginning of the file (which is now inside plist)
1536 WMReleasePropList(plist);
1537 plist = NULL;
1540 cleanup:
1541 wfree(pldata->ptr);
1542 wfree(pldata);
1543 fclose(f);
1545 return plist;
1548 /* TODO: review this function's code */
1550 Bool WMWritePropListToFile(WMPropList * plist, char *path)
1552 char *thePath = NULL;
1553 char *desc;
1554 FILE *theFile;
1555 #ifdef HAVE_MKSTEMP
1556 int fd, mask;
1557 #endif
1559 if (!wmkdirhier(path))
1560 return False;
1562 /* Use the path name of the destination file as a prefix for the
1563 * mkstemp() call so that we can be sure that both files are on
1564 * the same filesystem and the subsequent rename() will work. */
1565 thePath = wstrconcat(path, ".XXXXXX");
1567 #ifdef HAVE_MKSTEMP
1568 if ((fd = mkstemp(thePath)) < 0) {
1569 werror(_("mkstemp (%s) failed"), thePath);
1570 goto failure;
1572 mask = umask(0);
1573 umask(mask);
1574 fchmod(fd, 0644 & ~mask);
1575 if ((theFile = fdopen(fd, "wb")) == NULL) {
1576 close(fd);
1578 #else
1579 if (mktemp(thePath) == NULL) {
1580 werror(_("mktemp (%s) failed"), thePath);
1581 goto failure;
1583 theFile = fopen(thePath, "wb");
1584 #endif
1586 if (theFile == NULL) {
1587 werror(_("open (%s) failed"), thePath);
1588 goto failure;
1591 desc = indentedDescription(plist, 0);
1593 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1594 werror(_("writing to file: %s failed"), thePath);
1595 wfree(desc);
1596 goto failure;
1599 wfree(desc);
1601 (void)fsync(fileno(theFile));
1602 if (fclose(theFile) != 0) {
1603 werror(_("fclose (%s) failed"), thePath);
1604 goto failure;
1607 /* If we used a temporary file, we still need to rename() it be the
1608 * real file. Also, we need to try to retain the file attributes of
1609 * the original file we are overwriting (if we are) */
1610 if (rename(thePath, path) != 0) {
1611 werror(_("rename ('%s' to '%s') failed"), thePath, path);
1612 goto failure;
1615 wfree(thePath);
1616 return True;
1618 failure:
1619 unlink(thePath);
1620 wfree(thePath);
1621 return False;
1625 * create a directory hierarchy
1627 * if the last octet of `path' is `/', the full path is
1628 * assumed to be a directory; otherwise path is assumed to be a
1629 * file, and the last component is stripped off. the rest is the
1630 * the hierarchy to be created.
1632 * refuses to create anything outside $GNUSTEP_USER_ROOT
1634 * returns 1 on success, 0 on failure
1636 int wmkdirhier(const char *path)
1638 char *t, *thePath = NULL, buf[1024];
1639 size_t p, plen;
1640 struct stat st;
1642 /* Only create directories under $GNUSTEP_USER_ROOT */
1643 if ((t = wusergnusteppath()) == NULL)
1644 return 0;
1645 if (strncmp(path, t, strlen(t)) != 0)
1646 return 0;
1648 thePath = wstrdup(path);
1649 /* Strip the trailing component if it is a file */
1650 p = strlen(thePath);
1651 while (p && thePath[p] != '/')
1652 thePath[p--] = '\0';
1654 thePath[p] = '\0';
1656 /* Shortcut if it already exists */
1657 if (stat(thePath, &st) == 0) {
1658 wfree(thePath);
1659 if (S_ISDIR(st.st_mode)) {
1660 /* Is a directory alright */
1661 return 1;
1662 } else {
1663 /* Exists, but not a directory, the caller
1664 * might just as well abort now */
1665 return 0;
1669 memset(buf, 0, sizeof(buf));
1670 strncpy(buf, t, sizeof(buf) - 1);
1671 p = strlen(buf);
1672 plen = strlen(thePath);
1674 do {
1675 while (p++ < plen && thePath[p] != '/')
1678 strncpy(buf, thePath, p);
1679 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1680 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1681 werror(_("Could not create component %s"), buf);
1682 wfree(thePath);
1683 return 0;
1685 } while (p < plen);
1687 wfree(thePath);
1688 return 1;
1691 /* ARGSUSED2 */
1692 static int wrmdirhier_fn(const char *path, const struct stat *st,
1693 int type, struct FTW *ftw)
1695 switch(type) {
1696 case FTW_D:
1697 break;
1698 case FTW_DP:
1699 return rmdir(path);
1700 break;
1701 case FTW_F:
1702 case FTW_SL:
1703 case FTW_SLN:
1704 return unlink(path);
1705 break;
1706 case FTW_DNR:
1707 case FTW_NS:
1708 default:
1709 return EPERM;
1712 /* NOTREACHED */
1713 return 0;
1717 * remove a directory hierarchy
1719 * refuses to remove anything outside $GNUSTEP_USER_ROOT
1721 * returns 1 on success, 0 on failure
1723 * TODO: revisit what's error and what's not
1725 * with inspirations from OpenBSD's bin/rm/rm.c
1727 int wrmdirhier(const char *path)
1729 struct stat st;
1730 int error;
1731 char *t;
1733 /* Only remove directories under $GNUSTEP_USER_ROOT */
1734 if ((t = wusergnusteppath()) == NULL)
1735 return EPERM;
1736 if (strncmp(path, t, strlen(t)) != 0)
1737 return EPERM;
1739 /* Shortcut if it doesn't exist to begin with */
1740 if (stat(path, &st) == -1)
1741 return ENOENT;
1743 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1745 return error;