wmgenmenu: Add French and Spanish translations
[wmaker-crm.git] / WINGs / proplist.c
blob9e0d12be4221ed4669f38014d14cc311e57127c7
2 #if __GLIBC__ && \
3 (_XOPEN_SOURCE && _XOPEN_SOURCE < 500) || \
4 !_XOPEN_SOURCE
5 #define _XOPEN_SOURCE 500 /* nftw */
6 #endif
8 #include <sys/types.h>
9 #include <sys/stat.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <ftw.h>
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <unistd.h>
21 #include "WUtil.h"
22 #include "wconfig.h"
24 typedef enum {
25 WPLString = 0x57504c01,
26 WPLData = 0x57504c02,
27 WPLArray = 0x57504c03,
28 WPLDictionary = 0x57504c04
29 } WPLType;
31 typedef struct W_PropList {
32 WPLType type;
34 union {
35 char *string;
36 WMData *data;
37 WMArray *array;
38 WMHashTable *dict;
39 } d;
41 int retainCount;
42 } W_PropList;
44 typedef struct PLData {
45 char *ptr;
46 int pos;
47 char *filename;
48 int lineNumber;
49 } PLData;
51 typedef struct StringBuffer {
52 char *str;
53 int size;
54 } 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,
72 (retainFunc) NULL,
73 (releaseFunc) NULL
76 static Bool caseSensitive = True;
78 #define BUFFERSIZE 8192
79 #define BUFFERSIZE_INCREMENT 1024
81 #if 0
82 # define DPUT(s) puts(s)
83 #else
84 # define DPUT(s)
85 #endif
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)=='/' \
93 || (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)
113 unsigned ret = 0;
114 unsigned ctr = 0;
115 const char *key;
116 int i, len;
118 switch (plist->type) {
119 case WPLString:
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 *);
126 /*while (*key) {
127 ret ^= tolower(*key++) << ctr;
128 ctr = (ctr + 1) % sizeof (char *);
129 } */
130 break;
132 case WPLData:
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 *);
139 break;
141 default:
142 wwarning(_("Only string or data is supported for a proplist dictionary key"));
143 wassertrv(False, 0);
144 break;
147 return ret;
150 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
152 WMPropList *key, *value;
153 WMHashEnumerator e;
154 int i;
156 plist->retainCount += count;
158 switch (plist->type) {
159 case WPLString:
160 case WPLData:
161 break;
162 case WPLArray:
163 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
164 retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
166 break;
167 case WPLDictionary:
168 e = WMEnumerateHashTable(plist->d.dict);
169 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
170 retainPropListByCount(key, count);
171 retainPropListByCount(value, count);
173 break;
174 default:
175 wwarning(_("Used proplist functions on non-WMPropLists objects"));
176 wassertrv(False, NULL);
177 break;
180 return plist;
183 static void releasePropListByCount(WMPropList * plist, int count)
185 WMPropList *key, *value;
186 WMHashEnumerator e;
187 int i;
189 plist->retainCount -= count;
191 switch (plist->type) {
192 case WPLString:
193 if (plist->retainCount < 1) {
194 wfree(plist->d.string);
195 wfree(plist);
197 break;
198 case WPLData:
199 if (plist->retainCount < 1) {
200 WMReleaseData(plist->d.data);
201 wfree(plist);
203 break;
204 case WPLArray:
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);
210 wfree(plist);
212 break;
213 case WPLDictionary:
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);
221 wfree(plist);
223 break;
224 default:
225 wwarning(_("Used proplist functions on non-WMPropLists objects"));
226 wassertr(False);
227 break;
231 static char *dataDescription(WMPropList * plist)
233 const unsigned char *data;
234 char *retVal;
235 int i, j, length;
237 data = WMDataBytes(plist->d.data);
238 length = WMGetDataLength(plist->d.data);
240 retVal = (char *)wmalloc(2 * length + length / 4 + 3);
242 retVal[0] = '<';
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 */
248 retVal[j++] = ' ';
251 retVal[j++] = '>';
252 retVal[j] = '\0';
254 return retVal;
257 static char *stringDescription(WMPropList * plist)
259 const char *str;
260 char *retVal, *sPtr, *dPtr;
261 int len, quote;
262 unsigned char ch;
264 str = plist->d.string;
266 if (strlen(str) == 0) {
267 return wstrdup("\"\"");
270 /* FIXME: make this work with unichars. */
272 quote = 0;
273 sPtr = (char *)str;
274 len = 0;
275 while ((ch = *sPtr)) {
276 if (!noquote(ch)) {
277 quote = 1;
278 if (charesc(ch))
279 len++;
280 else if (numesc(ch))
281 len += 3;
283 sPtr++;
284 len++;
287 if (quote)
288 len += 2;
290 retVal = (char *)wmalloc(len + 1);
292 sPtr = (char *)str;
293 dPtr = retVal;
295 if (quote)
296 *dPtr++ = '"';
298 while ((ch = *sPtr)) {
299 if (charesc(ch)) {
300 *(dPtr++) = '\\';
301 switch (ch) {
302 case '\a':
303 *dPtr = 'a';
304 break;
305 case '\b':
306 *dPtr = 'b';
307 break;
308 case '\t':
309 *dPtr = 't';
310 break;
311 case '\n':
312 *dPtr = 'n';
313 break;
314 case '\v':
315 *dPtr = 'v';
316 break;
317 case '\f':
318 *dPtr = 'f';
319 break;
320 default:
321 *dPtr = ch; /* " or \ */
323 } else if (numesc(ch)) {
324 *(dPtr++) = '\\';
325 *(dPtr++) = '0' + ((ch >> 6) & 07);
326 *(dPtr++) = '0' + ((ch >> 3) & 07);
327 *dPtr = '0' + (ch & 07);
328 } else {
329 *dPtr = ch;
331 sPtr++;
332 dPtr++;
335 if (quote)
336 *dPtr++ = '"';
338 *dPtr = '\0';
340 return retVal;
343 static char *description(WMPropList * plist)
345 WMPropList *key, *val;
346 char *retstr = NULL;
347 char *str, *tmp, *skey, *sval;
348 WMHashEnumerator e;
349 int i;
351 switch (plist->type) {
352 case WPLString:
353 retstr = stringDescription(plist);
354 break;
355 case WPLData:
356 retstr = dataDescription(plist);
357 break;
358 case WPLArray:
359 retstr = wstrdup("(");
360 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
361 str = description(WMGetFromArray(plist->d.array, i));
362 if (i == 0) {
363 retstr = wstrappend(retstr, str);
364 } else {
365 tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
366 sprintf(tmp, "%s, %s", retstr, str);
367 wfree(retstr);
368 retstr = tmp;
370 wfree(str);
372 retstr = wstrappend(retstr, ")");
373 break;
374 case WPLDictionary:
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);
382 wfree(skey);
383 wfree(sval);
384 wfree(retstr);
385 retstr = tmp;
387 retstr = wstrappend(retstr, "}");
388 break;
389 default:
390 wwarning(_("Used proplist functions on non-WMPropLists objects"));
391 wassertrv(False, NULL);
392 break;
395 return retstr;
398 static char *indentedDescription(WMPropList * plist, int level)
400 WMPropList *key, *val;
401 char *retstr = NULL;
402 char *str, *tmp, *skey, *sval;
403 WMHashEnumerator e;
404 int i;
406 if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
407 retstr = description(plist);
409 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
410 return retstr;
411 } else if (retstr) {
412 wfree(retstr);
413 retstr = NULL;
417 switch (plist->type) {
418 case WPLString:
419 retstr = stringDescription(plist);
420 break;
421 case WPLData:
422 retstr = dataDescription(plist);
423 break;
424 case WPLArray:
425 retstr = wstrdup("(\n");
426 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
427 str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
428 if (i == 0) {
429 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
430 sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
431 wfree(retstr);
432 retstr = tmp;
433 } else {
434 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
435 sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
436 wfree(retstr);
437 retstr = tmp;
439 wfree(str);
441 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
442 sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
443 wfree(retstr);
444 retstr = tmp;
445 break;
446 case WPLDictionary:
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)
453 + strlen(sval) + 6);
454 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
455 wfree(skey);
456 wfree(sval);
457 wfree(retstr);
458 retstr = tmp;
460 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
461 sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
462 wfree(retstr);
463 retstr = tmp;
464 break;
465 default:
466 wwarning(_("Used proplist functions on non-WMPropLists objects"));
467 wassertrv(False, NULL);
468 break;
471 return retstr;
474 static INLINE int getChar(PLData * pldata)
476 int c;
478 c = pldata->ptr[pldata->pos];
479 if (c == 0) {
480 return 0;
483 pldata->pos++;
485 if (c == '\n')
486 pldata->lineNumber++;
488 return c;
491 static INLINE int getNonSpaceChar(PLData * pldata)
493 int c;
495 while (1) {
496 c = pldata->ptr[pldata->pos];
497 if (c == 0) {
498 break;
500 pldata->pos++;
501 if (c == '\n') {
502 pldata->lineNumber++;
503 } else if (!isspace(c)) {
504 break;
508 return c;
511 static char *unescapestr(char *src)
513 char *dest = wmalloc(strlen(src) + 1);
514 char *sPtr, *dPtr;
515 char ch;
517 for (sPtr = src, dPtr = dest; *sPtr; sPtr++, dPtr++) {
518 if (*sPtr != '\\') {
519 *dPtr = *sPtr;
520 } else {
521 ch = *(++sPtr);
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;
527 } else {
528 switch (ch) {
529 case 'a':
530 *dPtr = '\a';
531 break;
532 case 'b':
533 *dPtr = '\b';
534 break;
535 case 't':
536 *dPtr = '\t';
537 break;
538 case 'r':
539 *dPtr = '\r';
540 break;
541 case 'n':
542 *dPtr = '\n';
543 break;
544 case 'v':
545 *dPtr = '\v';
546 break;
547 case 'f':
548 *dPtr = '\f';
549 break;
550 default:
551 *dPtr = *sPtr;
557 *dPtr = 0;
559 return dest;
562 static WMPropList *getPLString(PLData * pldata)
564 WMPropList *plist;
565 StringBuffer sBuf;
566 int ptr = 0;
567 int c;
569 sBuf.str = wmalloc(BUFFERSIZE);
570 sBuf.size = BUFFERSIZE;
572 while (1) {
573 c = getChar(pldata);
574 if (ISSTRINGABLE(c)) {
575 CHECK_BUFFER_SIZE(sBuf, ptr);
576 sBuf.str[ptr++] = c;
577 } else {
578 if (c != 0) {
579 pldata->pos--;
581 break;
585 sBuf.str[ptr] = 0;
587 if (ptr == 0) {
588 plist = NULL;
589 } else {
590 char *tmp = unescapestr(sBuf.str);
591 plist = WMCreatePLString(tmp);
592 wfree(tmp);
595 wfree(sBuf.str);
597 return plist;
600 static WMPropList *getPLQString(PLData * pldata)
602 WMPropList *plist;
603 int ptr = 0, escaping = 0, ok = 1;
604 int c;
605 StringBuffer sBuf;
607 sBuf.str = wmalloc(BUFFERSIZE);
608 sBuf.size = BUFFERSIZE;
610 while (1) {
611 c = getChar(pldata);
612 if (!escaping) {
613 if (c == '\\') {
614 escaping = 1;
615 continue;
616 } else if (c == '"') {
617 break;
619 } else {
620 CHECK_BUFFER_SIZE(sBuf, ptr);
621 sBuf.str[ptr++] = '\\';
622 escaping = 0;
625 if (c == 0) {
626 COMPLAIN(pldata, _("unterminated PropList string"));
627 ok = 0;
628 break;
629 } else {
630 CHECK_BUFFER_SIZE(sBuf, ptr);
631 sBuf.str[ptr++] = c;
635 sBuf.str[ptr] = 0;
637 if (!ok) {
638 plist = NULL;
639 } else {
640 char *tmp = unescapestr(sBuf.str);
641 plist = WMCreatePLString(tmp);
642 wfree(tmp);
645 wfree(sBuf.str);
647 return plist;
650 static WMPropList *getPLData(PLData * pldata)
652 int ok = 1;
653 int len = 0;
654 int c1, c2;
655 unsigned char buf[BUFFERSIZE], byte;
656 WMPropList *plist;
657 WMData *data;
659 data = WMCreateDataWithCapacity(0);
661 while (1) {
662 c1 = getNonSpaceChar(pldata);
663 if (c1 == 0) {
664 COMPLAIN(pldata, _("unterminated PropList data"));
665 ok = 0;
666 break;
667 } else if (c1 == '>') {
668 break;
669 } else if (ishexdigit(c1)) {
670 c2 = getNonSpaceChar(pldata);
671 if (c2 == 0 || c2 == '>') {
672 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
673 ok = 0;
674 break;
675 } else if (ishexdigit(c2)) {
676 byte = char2num(c1) << 4;
677 byte |= char2num(c2);
678 buf[len++] = byte;
679 if (len == sizeof(buf)) {
680 WMAppendDataBytes(data, buf, len);
681 len = 0;
683 } else {
684 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
685 ok = 0;
686 break;
688 } else {
689 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
690 ok = 0;
691 break;
695 if (!ok) {
696 WMReleaseData(data);
697 return NULL;
700 if (len > 0)
701 WMAppendDataBytes(data, buf, len);
703 plist = WMCreatePLData(data);
704 WMReleaseData(data);
706 return plist;
709 static WMPropList *getPLArray(PLData * pldata)
711 Bool first = True;
712 int ok = 1;
713 int c;
714 WMPropList *array, *obj;
716 array = WMCreatePLArray(NULL);
718 while (1) {
719 c = getNonSpaceChar(pldata);
720 if (c == 0) {
721 COMPLAIN(pldata, _("unterminated PropList array"));
722 ok = 0;
723 break;
724 } else if (c == ')') {
725 break;
726 } else if (c == ',') {
727 /* continue normally */
728 } else if (!first) {
729 COMPLAIN(pldata, _("missing or unterminated PropList array"));
730 ok = 0;
731 break;
732 } else {
733 pldata->pos--;
735 first = False;
737 obj = getPropList(pldata);
738 if (!obj) {
739 COMPLAIN(pldata, _("could not get PropList array element"));
740 ok = 0;
741 break;
743 WMAddToPLArray(array, obj);
744 WMReleasePropList(obj);
747 if (!ok) {
748 WMReleasePropList(array);
749 array = NULL;
752 return array;
755 static WMPropList *getPLDictionary(PLData * pldata)
757 int ok = 1;
758 int c;
759 WMPropList *dict, *key, *value;
761 dict = WMCreatePLDictionary(NULL, NULL);
763 while (1) {
764 c = getNonSpaceChar(pldata);
765 if (c == 0) {
766 COMPLAIN(pldata, _("unterminated PropList dictionary"));
767 ok = 0;
768 break;
769 } else if (c == '}') {
770 break;
773 DPUT("getting PropList dictionary key");
774 if (c == '<') {
775 key = getPLData(pldata);
776 } else if (c == '"') {
777 key = getPLQString(pldata);
778 } else if (ISSTRINGABLE(c)) {
779 pldata->pos--;
780 key = getPLString(pldata);
781 } else {
782 if (c == '=') {
783 COMPLAIN(pldata, _("missing PropList dictionary key"));
784 } else {
785 COMPLAIN(pldata, _("missing PropList dictionary entry key "
786 "or unterminated dictionary"));
788 ok = 0;
789 break;
792 if (!key) {
793 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
794 ok = 0;
795 break;
798 c = getNonSpaceChar(pldata);
799 if (c != '=') {
800 WMReleasePropList(key);
801 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
802 ok = 0;
803 break;
806 DPUT("getting PropList dictionary entry value for key");
807 value = getPropList(pldata);
808 if (!value) {
809 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
810 WMReleasePropList(key);
811 ok = 0;
812 break;
815 c = getNonSpaceChar(pldata);
816 if (c != ';') {
817 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
818 WMReleasePropList(key);
819 WMReleasePropList(value);
820 ok = 0;
821 break;
824 WMPutInPLDictionary(dict, key, value);
825 WMReleasePropList(key);
826 WMReleasePropList(value);
829 if (!ok) {
830 WMReleasePropList(dict);
831 dict = NULL;
834 return dict;
837 static WMPropList *getPropList(PLData * pldata)
839 WMPropList *plist;
840 int c;
842 c = getNonSpaceChar(pldata);
844 switch (c) {
845 case 0:
846 DPUT("End of PropList");
847 plist = NULL;
848 break;
850 case '{':
851 DPUT("Getting PropList dictionary");
852 plist = getPLDictionary(pldata);
853 break;
855 case '(':
856 DPUT("Getting PropList array");
857 plist = getPLArray(pldata);
858 break;
860 case '<':
861 DPUT("Getting PropList data");
862 plist = getPLData(pldata);
863 break;
865 case '"':
866 DPUT("Getting PropList quoted string");
867 plist = getPLQString(pldata);
868 break;
870 default:
871 if (ISSTRINGABLE(c)) {
872 DPUT("Getting PropList string");
873 pldata->pos--;
874 plist = getPLString(pldata);
875 } else {
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."));
881 plist = NULL;
883 break;
886 return plist;
889 void WMPLSetCaseSensitive(Bool caseSensitiveness)
891 caseSensitive = caseSensitiveness;
894 WMPropList *WMCreatePLString(char *str)
896 WMPropList *plist;
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;
905 return plist;
908 WMPropList *WMCreatePLData(WMData * data)
910 WMPropList *plist;
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;
919 return plist;
922 WMPropList *WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
924 WMPropList *plist;
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;
933 return plist;
936 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
938 WMPropList *plist;
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;
947 return plist;
950 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
952 WMPropList *plist, *nelem;
953 va_list ap;
955 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
956 plist->type = WPLArray;
957 plist->d.array = WMCreateArray(4);
958 plist->retainCount = 1;
960 if (!elem)
961 return plist;
963 WMAddToArray(plist->d.array, WMRetainPropList(elem));
965 va_start(ap, elem);
967 while (1) {
968 nelem = va_arg(ap, WMPropList *);
969 if (!nelem) {
970 va_end(ap);
971 return plist;
973 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
977 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
979 WMPropList *plist, *nkey, *nvalue, *k, *v;
980 va_list ap;
982 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
983 plist->type = WPLDictionary;
984 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
985 plist->retainCount = 1;
987 if (!key || !value)
988 return plist;
990 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
992 va_start(ap, value);
994 while (1) {
995 nkey = va_arg(ap, WMPropList *);
996 if (!nkey) {
997 va_end(ap);
998 return plist;
1000 nvalue = va_arg(ap, WMPropList *);
1001 if (!nvalue) {
1002 va_end(ap);
1003 return plist;
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;
1017 WMHashEnumerator e;
1018 int i;
1020 plist->retainCount++;
1022 switch (plist->type) {
1023 case WPLString:
1024 case WPLData:
1025 break;
1026 case WPLArray:
1027 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1028 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1030 break;
1031 case WPLDictionary:
1032 e = WMEnumerateHashTable(plist->d.dict);
1033 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1034 WMRetainPropList(key);
1035 WMRetainPropList(value);
1037 break;
1038 default:
1039 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1040 wassertrv(False, NULL);
1041 break;
1044 return plist;
1047 void WMReleasePropList(WMPropList * plist)
1049 WMPropList *key, *value;
1050 WMHashEnumerator e;
1051 int i;
1053 plist->retainCount--;
1055 switch (plist->type) {
1056 case WPLString:
1057 if (plist->retainCount < 1) {
1058 wfree(plist->d.string);
1059 wfree(plist);
1061 break;
1062 case WPLData:
1063 if (plist->retainCount < 1) {
1064 WMReleaseData(plist->d.data);
1065 wfree(plist);
1067 break;
1068 case WPLArray:
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);
1074 wfree(plist);
1076 break;
1077 case WPLDictionary:
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);
1085 wfree(plist);
1087 break;
1088 default:
1089 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1090 wassertr(False);
1091 break;
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)
1113 WMPropList *item;
1115 wassertr(plist->type == WPLArray);
1117 item = WMGetFromArray(plist->d.array, index);
1118 if (item != NULL) {
1119 WMDeleteFromArray(plist->d.array, index);
1120 releasePropListByCount(item, plist->retainCount);
1124 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1126 WMPropList *iPtr;
1127 int i;
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);
1136 break;
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)
1155 WMPropList *k, *v;
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;
1169 WMHashEnumerator e;
1171 wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1173 if (source == dest)
1174 return dest;
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);
1182 } else {
1183 WMPutInPLDictionary(dest, key, value);
1185 } else {
1186 WMPutInPLDictionary(dest, key, value);
1190 return dest;
1193 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1195 WMPropList *key, *value, *dvalue;
1196 WMHashEnumerator e;
1198 wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1200 if (source == dest) {
1201 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1202 int i;
1204 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1205 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1207 return dest;
1210 e = WMEnumerateHashTable(source->d.dict);
1211 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1212 dvalue = WMHashGet(dest->d.dict, key);
1213 if (!dvalue)
1214 continue;
1215 if (WMIsPropListEqualTo(value, dvalue)) {
1216 WMRemoveFromPLDictionary(dest, key);
1217 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1218 WMSubtractPLDictionaries(dvalue, value, True);
1222 return dest;
1225 int WMGetPropListItemCount(WMPropList * plist)
1227 switch (plist->type) {
1228 case WPLString:
1229 case WPLData:
1230 return 0; /* should this be 1 instead? */
1231 case WPLArray:
1232 return WMGetArrayItemCount(plist->d.array);
1233 case WPLDictionary:
1234 return (int)WMCountHashTable(plist->d.dict);
1235 default:
1236 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1237 wassertrv(False, 0);
1238 break;
1241 return 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;
1268 int n, i;
1270 if (plist->type != other->type)
1271 return False;
1273 switch (plist->type) {
1274 case WPLString:
1275 if (caseSensitive) {
1276 return (strcmp(plist->d.string, other->d.string) == 0);
1277 } else {
1278 return (strcasecmp(plist->d.string, other->d.string) == 0);
1280 case WPLData:
1281 return WMIsDataEqualToData(plist->d.data, other->d.data);
1282 case WPLArray:
1283 n = WMGetArrayItemCount(plist->d.array);
1284 if (n != WMGetArrayItemCount(other->d.array))
1285 return False;
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))
1290 return False;
1292 return True;
1293 case WPLDictionary:
1294 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1295 return False;
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))
1300 return False;
1302 return True;
1303 default:
1304 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1305 wassertrv(False, False);
1306 break;
1309 return 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));
1371 return array;
1374 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1376 WMPropList *ret = NULL;
1377 WMPropList *key, *item;
1378 WMHashEnumerator e;
1379 WMData *data;
1380 int i;
1382 switch (plist->type) {
1383 case WPLString:
1384 ret = WMCreatePLString(plist->d.string);
1385 break;
1386 case WPLData:
1387 data = WMCreateDataWithData(plist->d.data);
1388 ret = WMCreatePLData(data);
1389 WMReleaseData(data);
1390 break;
1391 case WPLArray:
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));
1400 break;
1401 case WPLDictionary:
1402 ret = WMCreatePLDictionary(NULL, NULL);
1403 e = WMEnumerateHashTable(plist->d.dict);
1404 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1405 WMPutInPLDictionary(ret, key, item);
1407 break;
1408 default:
1409 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1410 wassertrv(False, NULL);
1411 break;
1414 return ret;
1417 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1419 WMPropList *ret = NULL;
1420 WMPropList *key, *item;
1421 WMHashEnumerator e;
1422 WMData *data;
1423 int i;
1425 switch (plist->type) {
1426 case WPLString:
1427 ret = WMCreatePLString(plist->d.string);
1428 break;
1429 case WPLData:
1430 data = WMCreateDataWithData(plist->d.data);
1431 ret = WMCreatePLData(data);
1432 WMReleaseData(data);
1433 break;
1434 case WPLArray:
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);
1440 break;
1441 case WPLDictionary:
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));
1451 break;
1452 default:
1453 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1454 wassertrv(False, NULL);
1455 break;
1458 return ret;
1461 WMPropList *WMCreatePropListFromDescription(char *desc)
1463 WMPropList *plist = NULL;
1464 PLData *pldata;
1466 pldata = (PLData *) wmalloc(sizeof(PLData));
1467 memset(pldata, 0, sizeof(PLData));
1468 pldata->ptr = desc;
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);
1482 plist = NULL;
1485 wfree(pldata);
1487 return 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;
1498 PLData *pldata;
1499 FILE *f;
1500 struct stat stbuf;
1501 size_t length;
1503 f = fopen(file, "rb");
1504 if (!f) {
1505 /* let the user print the error message if he really needs to */
1506 /*werror(_("could not open domain file '%s' for reading"), file); */
1507 return NULL;
1510 if (stat(file, &stbuf) == 0) {
1511 length = (size_t) stbuf.st_size;
1512 } else {
1513 werror(_("could not get size for file '%s'"), file);
1514 fclose(f);
1515 return NULL;
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) {
1525 if (ferror(f)) {
1526 werror(_("error reading from file '%s'"), file);
1528 plist = NULL;
1529 goto cleanup;
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);
1545 plist = NULL;
1548 cleanup:
1549 wfree(pldata->ptr);
1550 wfree(pldata);
1551 fclose(f);
1553 return plist;
1556 /* TODO: review this function's code */
1558 Bool WMWritePropListToFile(WMPropList * plist, char *path)
1560 char *thePath = NULL;
1561 char *desc;
1562 FILE *theFile;
1563 #ifdef HAVE_MKSTEMP
1564 int fd, mask;
1565 #endif
1567 if (!wmkdirhier(path))
1568 return False;
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");
1575 #ifdef HAVE_MKSTEMP
1576 if ((fd = mkstemp(thePath)) < 0) {
1577 werror(_("mkstemp (%s) failed"), thePath);
1578 goto failure;
1580 mask = umask(0);
1581 umask(mask);
1582 fchmod(fd, 0644 & ~mask);
1583 if ((theFile = fdopen(fd, "wb")) == NULL) {
1584 close(fd);
1586 #else
1587 if (mktemp(thePath) == NULL) {
1588 werror(_("mktemp (%s) failed"), thePath);
1589 goto failure;
1591 theFile = fopen(thePath, "wb");
1592 #endif
1594 if (theFile == NULL) {
1595 werror(_("open (%s) failed"), thePath);
1596 goto failure;
1599 desc = indentedDescription(plist, 0);
1601 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1602 werror(_("writing to file: %s failed"), thePath);
1603 wfree(desc);
1604 goto failure;
1607 wfree(desc);
1609 (void)fsync(fileno(theFile));
1610 if (fclose(theFile) != 0) {
1611 werror(_("fclose (%s) failed"), thePath);
1612 goto failure;
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);
1620 goto failure;
1623 wfree(thePath);
1624 return True;
1626 failure:
1627 unlink(thePath);
1628 wfree(thePath);
1629 return False;
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];
1647 size_t p, plen;
1648 struct stat st;
1650 /* Only create directories under $GNUSTEP_USER_ROOT */
1651 if ((t = wusergnusteppath()) == NULL)
1652 return 0;
1653 if (strncmp(path, t, strlen(t)) != 0)
1654 return 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';
1662 thePath[p] = '\0';
1664 /* Shortcut if it already exists */
1665 if (stat(thePath, &st) == 0) {
1666 wfree(thePath);
1667 if (S_ISDIR(st.st_mode)) {
1668 /* Is a directory alright */
1669 return 1;
1670 } else {
1671 /* Exists, but not a directory, the caller
1672 * might just as well abort now */
1673 return 0;
1677 memset(buf, 0, sizeof(buf));
1678 strncpy(buf, t, sizeof(buf) - 1);
1679 p = strlen(buf);
1680 plen = strlen(thePath);
1682 do {
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);
1690 wfree(thePath);
1691 return 0;
1693 } while (p < plen);
1695 wfree(thePath);
1696 return 1;
1699 /* ARGSUSED2 */
1700 static int wrmdirhier_fn(const char *path, const struct stat *st,
1701 int type, struct FTW *ftw)
1703 switch(type) {
1704 case FTW_D:
1705 break;
1706 case FTW_DP:
1707 return rmdir(path);
1708 break;
1709 case FTW_F:
1710 case FTW_SL:
1711 case FTW_SLN:
1712 return unlink(path);
1713 break;
1714 case FTW_DNR:
1715 case FTW_NS:
1716 default:
1717 return EPERM;
1720 /* NOTREACHED */
1721 return 0;
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)
1737 struct stat st;
1738 int error;
1739 char *t;
1741 /* Only remove directories under $GNUSTEP_USER_ROOT */
1742 if ((t = wusergnusteppath()) == NULL)
1743 return EPERM;
1744 if (strncmp(path, t, strlen(t)) != 0)
1745 return EPERM;
1747 /* Shortcut if it doesn't exist to begin with */
1748 if (stat(path, &st) == -1)
1749 return ENOENT;
1751 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1753 return error;