Fix spacing on WPrefs Misc panel
[wmaker-crm.git] / WINGs / proplist.c
blob476fa659c31050cfb826ad526afd3829454e0d29
2 #include <errno.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <ctype.h>
12 #include <fts.h>
14 #include "WUtil.h"
15 #include "wconfig.h"
17 typedef enum {
18 WPLString = 0x57504c01,
19 WPLData = 0x57504c02,
20 WPLArray = 0x57504c03,
21 WPLDictionary = 0x57504c04
22 } WPLType;
24 typedef struct W_PropList {
25 WPLType type;
27 union {
28 char *string;
29 WMData *data;
30 WMArray *array;
31 WMHashTable *dict;
32 } d;
34 int retainCount;
35 } W_PropList;
37 typedef struct PLData {
38 char *ptr;
39 int pos;
40 char *filename;
41 int lineNumber;
42 } PLData;
44 typedef struct StringBuffer {
45 char *str;
46 int size;
47 } StringBuffer;
49 static unsigned hashPropList(WMPropList * plist);
50 static WMPropList *getPLString(PLData * pldata);
51 static WMPropList *getPLQString(PLData * pldata);
52 static WMPropList *getPLData(PLData * pldata);
53 static WMPropList *getPLArray(PLData * pldata);
54 static WMPropList *getPLDictionary(PLData * pldata);
55 static WMPropList *getPropList(PLData * pldata);
57 typedef unsigned (*hashFunc) (const void *);
58 typedef Bool(*isEqualFunc) (const void *, const void *);
59 typedef void *(*retainFunc) (const void *);
60 typedef void (*releaseFunc) (const void *);
62 static const WMHashTableCallbacks WMPropListHashCallbacks = {
63 (hashFunc) hashPropList,
64 (isEqualFunc) WMIsPropListEqualTo,
65 (retainFunc) NULL,
66 (releaseFunc) NULL
69 static Bool caseSensitive = True;
71 #define BUFFERSIZE 8192
72 #define BUFFERSIZE_INCREMENT 1024
74 #if 0
75 # define DPUT(s) puts(s)
76 #else
77 # define DPUT(s)
78 #endif
80 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
81 (pld)->filename ? "file" : "PropList",\
82 (pld)->filename ? (pld)->filename : "description",\
83 (pld)->lineNumber, msg)
85 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
86 || (c)=='+')
88 #define CHECK_BUFFER_SIZE(buf, ptr) \
89 if ((ptr) >= (buf).size-1) {\
90 (buf).size += BUFFERSIZE_INCREMENT;\
91 (buf).str = wrealloc((buf).str, (buf).size);\
94 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
95 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
96 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
97 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
98 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
99 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
100 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
102 #define MaxHashLength 64
104 static unsigned hashPropList(WMPropList * plist)
106 unsigned ret = 0;
107 unsigned ctr = 0;
108 const char *key;
109 int i, len;
111 switch (plist->type) {
112 case WPLString:
113 key = plist->d.string;
114 len = WMIN(strlen(key), MaxHashLength);
115 for (i = 0; i < len; i++) {
116 ret ^= tolower(key[i]) << ctr;
117 ctr = (ctr + 1) % sizeof(char *);
119 /*while (*key) {
120 ret ^= tolower(*key++) << ctr;
121 ctr = (ctr + 1) % sizeof (char *);
122 } */
123 break;
125 case WPLData:
126 key = WMDataBytes(plist->d.data);
127 len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
128 for (i = 0; i < len; i++) {
129 ret ^= key[i] << ctr;
130 ctr = (ctr + 1) % sizeof(char *);
132 break;
134 default:
135 wwarning(_("Only string or data is supported for a proplist dictionary key"));
136 wassertrv(False, 0);
137 break;
140 return ret;
143 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
145 WMPropList *key, *value;
146 WMHashEnumerator e;
147 int i;
149 plist->retainCount += count;
151 switch (plist->type) {
152 case WPLString:
153 case WPLData:
154 break;
155 case WPLArray:
156 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
157 retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
159 break;
160 case WPLDictionary:
161 e = WMEnumerateHashTable(plist->d.dict);
162 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
163 retainPropListByCount(key, count);
164 retainPropListByCount(value, count);
166 break;
167 default:
168 wwarning(_("Used proplist functions on non-WMPropLists objects"));
169 wassertrv(False, NULL);
170 break;
173 return plist;
176 static void releasePropListByCount(WMPropList * plist, int count)
178 WMPropList *key, *value;
179 WMHashEnumerator e;
180 int i;
182 plist->retainCount -= count;
184 switch (plist->type) {
185 case WPLString:
186 if (plist->retainCount < 1) {
187 wfree(plist->d.string);
188 wfree(plist);
190 break;
191 case WPLData:
192 if (plist->retainCount < 1) {
193 WMReleaseData(plist->d.data);
194 wfree(plist);
196 break;
197 case WPLArray:
198 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
199 releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
201 if (plist->retainCount < 1) {
202 WMFreeArray(plist->d.array);
203 wfree(plist);
205 break;
206 case WPLDictionary:
207 e = WMEnumerateHashTable(plist->d.dict);
208 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
209 releasePropListByCount(key, count);
210 releasePropListByCount(value, count);
212 if (plist->retainCount < 1) {
213 WMFreeHashTable(plist->d.dict);
214 wfree(plist);
216 break;
217 default:
218 wwarning(_("Used proplist functions on non-WMPropLists objects"));
219 wassertr(False);
220 break;
224 static char *dataDescription(WMPropList * plist)
226 const unsigned char *data;
227 char *retVal;
228 int i, j, length;
230 data = WMDataBytes(plist->d.data);
231 length = WMGetDataLength(plist->d.data);
233 retVal = (char *)wmalloc(2 * length + length / 4 + 3);
235 retVal[0] = '<';
236 for (i = 0, j = 1; i < length; i++) {
237 retVal[j++] = num2char((data[i] >> 4) & 0x0f);
238 retVal[j++] = num2char(data[i] & 0x0f);
239 if ((i & 0x03) == 3 && i != length - 1) {
240 /* if we've just finished a 32-bit int, add a space */
241 retVal[j++] = ' ';
244 retVal[j++] = '>';
245 retVal[j] = '\0';
247 return retVal;
250 static char *stringDescription(WMPropList * plist)
252 const char *str;
253 char *retVal, *sPtr, *dPtr;
254 int len, quote;
255 unsigned char ch;
257 str = plist->d.string;
259 if (strlen(str) == 0) {
260 return wstrdup("\"\"");
263 /* FIXME: make this work with unichars. */
265 quote = 0;
266 sPtr = (char *)str;
267 len = 0;
268 while ((ch = *sPtr)) {
269 if (!noquote(ch)) {
270 quote = 1;
271 if (charesc(ch))
272 len++;
273 else if (numesc(ch))
274 len += 3;
276 sPtr++;
277 len++;
280 if (quote)
281 len += 2;
283 retVal = (char *)wmalloc(len + 1);
285 sPtr = (char *)str;
286 dPtr = retVal;
288 if (quote)
289 *dPtr++ = '"';
291 while ((ch = *sPtr)) {
292 if (charesc(ch)) {
293 *(dPtr++) = '\\';
294 switch (ch) {
295 case '\a':
296 *dPtr = 'a';
297 break;
298 case '\b':
299 *dPtr = 'b';
300 break;
301 case '\t':
302 *dPtr = 't';
303 break;
304 case '\n':
305 *dPtr = 'n';
306 break;
307 case '\v':
308 *dPtr = 'v';
309 break;
310 case '\f':
311 *dPtr = 'f';
312 break;
313 default:
314 *dPtr = ch; /* " or \ */
316 } else if (numesc(ch)) {
317 *(dPtr++) = '\\';
318 *(dPtr++) = '0' + ((ch >> 6) & 07);
319 *(dPtr++) = '0' + ((ch >> 3) & 07);
320 *dPtr = '0' + (ch & 07);
321 } else {
322 *dPtr = ch;
324 sPtr++;
325 dPtr++;
328 if (quote)
329 *dPtr++ = '"';
331 *dPtr = '\0';
333 return retVal;
336 static char *description(WMPropList * plist)
338 WMPropList *key, *val;
339 char *retstr = NULL;
340 char *str, *tmp, *skey, *sval;
341 WMHashEnumerator e;
342 int i;
344 switch (plist->type) {
345 case WPLString:
346 retstr = stringDescription(plist);
347 break;
348 case WPLData:
349 retstr = dataDescription(plist);
350 break;
351 case WPLArray:
352 retstr = wstrdup("(");
353 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
354 str = description(WMGetFromArray(plist->d.array, i));
355 if (i == 0) {
356 retstr = wstrappend(retstr, str);
357 } else {
358 tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
359 sprintf(tmp, "%s, %s", retstr, str);
360 wfree(retstr);
361 retstr = tmp;
363 wfree(str);
365 retstr = wstrappend(retstr, ")");
366 break;
367 case WPLDictionary:
368 retstr = wstrdup("{");
369 e = WMEnumerateHashTable(plist->d.dict);
370 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
371 skey = description(key);
372 sval = description(val);
373 tmp = (char *)wmalloc(strlen(retstr) + strlen(skey) + strlen(sval) + 5);
374 sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
375 wfree(skey);
376 wfree(sval);
377 wfree(retstr);
378 retstr = tmp;
380 retstr = wstrappend(retstr, "}");
381 break;
382 default:
383 wwarning(_("Used proplist functions on non-WMPropLists objects"));
384 wassertrv(False, NULL);
385 break;
388 return retstr;
391 static char *indentedDescription(WMPropList * plist, int level)
393 WMPropList *key, *val;
394 char *retstr = NULL;
395 char *str, *tmp, *skey, *sval;
396 WMHashEnumerator e;
397 int i;
399 if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
400 retstr = description(plist);
402 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
403 return retstr;
404 } else if (retstr) {
405 wfree(retstr);
406 retstr = NULL;
410 switch (plist->type) {
411 case WPLString:
412 retstr = stringDescription(plist);
413 break;
414 case WPLData:
415 retstr = dataDescription(plist);
416 break;
417 case WPLArray:
418 retstr = wstrdup("(\n");
419 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
420 str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
421 if (i == 0) {
422 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
423 sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
424 wfree(retstr);
425 retstr = tmp;
426 } else {
427 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
428 sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
429 wfree(retstr);
430 retstr = tmp;
432 wfree(str);
434 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
435 sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
436 wfree(retstr);
437 retstr = tmp;
438 break;
439 case WPLDictionary:
440 retstr = wstrdup("{\n");
441 e = WMEnumerateHashTable(plist->d.dict);
442 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
443 skey = indentedDescription(key, level + 1);
444 sval = indentedDescription(val, level + 1);
445 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(skey)
446 + strlen(sval) + 6);
447 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
448 wfree(skey);
449 wfree(sval);
450 wfree(retstr);
451 retstr = tmp;
453 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
454 sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
455 wfree(retstr);
456 retstr = tmp;
457 break;
458 default:
459 wwarning(_("Used proplist functions on non-WMPropLists objects"));
460 wassertrv(False, NULL);
461 break;
464 return retstr;
467 static INLINE int getChar(PLData * pldata)
469 int c;
471 c = pldata->ptr[pldata->pos];
472 if (c == 0) {
473 return 0;
476 pldata->pos++;
478 if (c == '\n')
479 pldata->lineNumber++;
481 return c;
484 static INLINE int getNonSpaceChar(PLData * pldata)
486 int c;
488 while (1) {
489 c = pldata->ptr[pldata->pos];
490 if (c == 0) {
491 break;
493 pldata->pos++;
494 if (c == '\n') {
495 pldata->lineNumber++;
496 } else if (!isspace(c)) {
497 break;
501 return c;
504 static char *unescapestr(char *src)
506 char *dest = wmalloc(strlen(src) + 1);
507 char *sPtr, *dPtr;
508 char ch;
510 for (sPtr = src, dPtr = dest; *sPtr; sPtr++, dPtr++) {
511 if (*sPtr != '\\') {
512 *dPtr = *sPtr;
513 } else {
514 ch = *(++sPtr);
515 if ((ch >= '0') && (ch <= '3')) {
516 /* assume next 2 chars are octal too */
517 *dPtr = ((ch & 07) << 6);
518 *dPtr |= ((*(++sPtr) & 07) << 3);
519 *dPtr |= *(++sPtr) & 07;
520 } else {
521 switch (ch) {
522 case 'a':
523 *dPtr = '\a';
524 break;
525 case 'b':
526 *dPtr = '\b';
527 break;
528 case 't':
529 *dPtr = '\t';
530 break;
531 case 'r':
532 *dPtr = '\r';
533 break;
534 case 'n':
535 *dPtr = '\n';
536 break;
537 case 'v':
538 *dPtr = '\v';
539 break;
540 case 'f':
541 *dPtr = '\f';
542 break;
543 default:
544 *dPtr = *sPtr;
550 *dPtr = 0;
552 return dest;
555 static WMPropList *getPLString(PLData * pldata)
557 WMPropList *plist;
558 StringBuffer sBuf;
559 int ptr = 0;
560 int c;
562 sBuf.str = wmalloc(BUFFERSIZE);
563 sBuf.size = BUFFERSIZE;
565 while (1) {
566 c = getChar(pldata);
567 if (ISSTRINGABLE(c)) {
568 CHECK_BUFFER_SIZE(sBuf, ptr);
569 sBuf.str[ptr++] = c;
570 } else {
571 if (c != 0) {
572 pldata->pos--;
574 break;
578 sBuf.str[ptr] = 0;
580 if (ptr == 0) {
581 plist = NULL;
582 } else {
583 char *tmp = unescapestr(sBuf.str);
584 plist = WMCreatePLString(tmp);
585 wfree(tmp);
588 wfree(sBuf.str);
590 return plist;
593 static WMPropList *getPLQString(PLData * pldata)
595 WMPropList *plist;
596 int ptr = 0, escaping = 0, ok = 1;
597 int c;
598 StringBuffer sBuf;
600 sBuf.str = wmalloc(BUFFERSIZE);
601 sBuf.size = BUFFERSIZE;
603 while (1) {
604 c = getChar(pldata);
605 if (!escaping) {
606 if (c == '\\') {
607 escaping = 1;
608 continue;
609 } else if (c == '"') {
610 break;
612 } else {
613 CHECK_BUFFER_SIZE(sBuf, ptr);
614 sBuf.str[ptr++] = '\\';
615 escaping = 0;
618 if (c == 0) {
619 COMPLAIN(pldata, _("unterminated PropList string"));
620 ok = 0;
621 break;
622 } else {
623 CHECK_BUFFER_SIZE(sBuf, ptr);
624 sBuf.str[ptr++] = c;
628 sBuf.str[ptr] = 0;
630 if (!ok) {
631 plist = NULL;
632 } else {
633 char *tmp = unescapestr(sBuf.str);
634 plist = WMCreatePLString(tmp);
635 wfree(tmp);
638 wfree(sBuf.str);
640 return plist;
643 static WMPropList *getPLData(PLData * pldata)
645 int ok = 1;
646 int len = 0;
647 int c1, c2;
648 unsigned char buf[BUFFERSIZE], byte;
649 WMPropList *plist;
650 WMData *data;
652 data = WMCreateDataWithCapacity(0);
654 while (1) {
655 c1 = getNonSpaceChar(pldata);
656 if (c1 == 0) {
657 COMPLAIN(pldata, _("unterminated PropList data"));
658 ok = 0;
659 break;
660 } else if (c1 == '>') {
661 break;
662 } else if (ishexdigit(c1)) {
663 c2 = getNonSpaceChar(pldata);
664 if (c2 == 0 || c2 == '>') {
665 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
666 ok = 0;
667 break;
668 } else if (ishexdigit(c2)) {
669 byte = char2num(c1) << 4;
670 byte |= char2num(c2);
671 buf[len++] = byte;
672 if (len == sizeof(buf)) {
673 WMAppendDataBytes(data, buf, len);
674 len = 0;
676 } else {
677 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
678 ok = 0;
679 break;
681 } else {
682 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
683 ok = 0;
684 break;
688 if (!ok) {
689 WMReleaseData(data);
690 return NULL;
693 if (len > 0)
694 WMAppendDataBytes(data, buf, len);
696 plist = WMCreatePLData(data);
697 WMReleaseData(data);
699 return plist;
702 static WMPropList *getPLArray(PLData * pldata)
704 Bool first = True;
705 int ok = 1;
706 int c;
707 WMPropList *array, *obj;
709 array = WMCreatePLArray(NULL);
711 while (1) {
712 c = getNonSpaceChar(pldata);
713 if (c == 0) {
714 COMPLAIN(pldata, _("unterminated PropList array"));
715 ok = 0;
716 break;
717 } else if (c == ')') {
718 break;
719 } else if (c == ',') {
720 /* continue normally */
721 } else if (!first) {
722 COMPLAIN(pldata, _("missing or unterminated PropList array"));
723 ok = 0;
724 break;
725 } else {
726 pldata->pos--;
728 first = False;
730 obj = getPropList(pldata);
731 if (!obj) {
732 COMPLAIN(pldata, _("could not get PropList array element"));
733 ok = 0;
734 break;
736 WMAddToPLArray(array, obj);
737 WMReleasePropList(obj);
740 if (!ok) {
741 WMReleasePropList(array);
742 array = NULL;
745 return array;
748 static WMPropList *getPLDictionary(PLData * pldata)
750 int ok = 1;
751 int c;
752 WMPropList *dict, *key, *value;
754 dict = WMCreatePLDictionary(NULL, NULL);
756 while (1) {
757 c = getNonSpaceChar(pldata);
758 if (c == 0) {
759 COMPLAIN(pldata, _("unterminated PropList dictionary"));
760 ok = 0;
761 break;
762 } else if (c == '}') {
763 break;
766 DPUT("getting PropList dictionary key");
767 if (c == '<') {
768 key = getPLData(pldata);
769 } else if (c == '"') {
770 key = getPLQString(pldata);
771 } else if (ISSTRINGABLE(c)) {
772 pldata->pos--;
773 key = getPLString(pldata);
774 } else {
775 if (c == '=') {
776 COMPLAIN(pldata, _("missing PropList dictionary key"));
777 } else {
778 COMPLAIN(pldata, _("missing PropList dictionary entry key "
779 "or unterminated dictionary"));
781 ok = 0;
782 break;
785 if (!key) {
786 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
787 ok = 0;
788 break;
791 c = getNonSpaceChar(pldata);
792 if (c != '=') {
793 WMReleasePropList(key);
794 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
795 ok = 0;
796 break;
799 DPUT("getting PropList dictionary entry value for key");
800 value = getPropList(pldata);
801 if (!value) {
802 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
803 WMReleasePropList(key);
804 ok = 0;
805 break;
808 c = getNonSpaceChar(pldata);
809 if (c != ';') {
810 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
811 WMReleasePropList(key);
812 WMReleasePropList(value);
813 ok = 0;
814 break;
817 WMPutInPLDictionary(dict, key, value);
818 WMReleasePropList(key);
819 WMReleasePropList(value);
822 if (!ok) {
823 WMReleasePropList(dict);
824 dict = NULL;
827 return dict;
830 static WMPropList *getPropList(PLData * pldata)
832 WMPropList *plist;
833 int c;
835 c = getNonSpaceChar(pldata);
837 switch (c) {
838 case 0:
839 DPUT("End of PropList");
840 plist = NULL;
841 break;
843 case '{':
844 DPUT("Getting PropList dictionary");
845 plist = getPLDictionary(pldata);
846 break;
848 case '(':
849 DPUT("Getting PropList array");
850 plist = getPLArray(pldata);
851 break;
853 case '<':
854 DPUT("Getting PropList data");
855 plist = getPLData(pldata);
856 break;
858 case '"':
859 DPUT("Getting PropList quoted string");
860 plist = getPLQString(pldata);
861 break;
863 default:
864 if (ISSTRINGABLE(c)) {
865 DPUT("Getting PropList string");
866 pldata->pos--;
867 plist = getPLString(pldata);
868 } else {
869 COMPLAIN(pldata, _("was expecting a string, data, array or "
870 "dictionary. If it's a string, try enclosing " "it with \"."));
871 if (c == '#' || c == '/') {
872 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
874 plist = NULL;
876 break;
879 return plist;
882 void WMPLSetCaseSensitive(Bool caseSensitiveness)
884 caseSensitive = caseSensitiveness;
887 WMPropList *WMCreatePLString(char *str)
889 WMPropList *plist;
891 wassertrv(str != NULL, NULL);
893 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));
910 plist->type = WPLData;
911 plist->d.data = WMRetainData(data);
912 plist->retainCount = 1;
914 return plist;
917 WMPropList *WMCreatePLDataWithBytes(unsigned char *bytes, unsigned int length)
919 WMPropList *plist;
921 wassertrv(bytes != NULL, NULL);
923 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
925 plist->type = WPLData;
926 plist->d.data = WMCreateDataWithBytes(bytes, length);
927 plist->retainCount = 1;
929 return plist;
932 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
934 WMPropList *plist;
936 wassertrv(bytes != NULL, NULL);
938 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
940 plist->type = WPLData;
941 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
942 plist->retainCount = 1;
944 return plist;
947 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
949 WMPropList *plist, *nelem;
950 va_list ap;
952 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
953 plist->type = WPLArray;
954 plist->d.array = WMCreateArray(4);
955 plist->retainCount = 1;
957 if (!elem)
958 return plist;
960 WMAddToArray(plist->d.array, WMRetainPropList(elem));
962 va_start(ap, elem);
964 while (1) {
965 nelem = va_arg(ap, WMPropList *);
966 if (!nelem) {
967 va_end(ap);
968 return plist;
970 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
974 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
976 WMPropList *plist, *nkey, *nvalue, *k, *v;
977 va_list ap;
979 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
980 plist->type = WPLDictionary;
981 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
982 plist->retainCount = 1;
984 if (!key || !value)
985 return plist;
987 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
989 va_start(ap, value);
991 while (1) {
992 nkey = va_arg(ap, WMPropList *);
993 if (!nkey) {
994 va_end(ap);
995 return plist;
997 nvalue = va_arg(ap, WMPropList *);
998 if (!nvalue) {
999 va_end(ap);
1000 return plist;
1002 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
1003 WMHashRemove(plist->d.dict, k);
1004 WMReleasePropList(k);
1005 WMReleasePropList(v);
1007 WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
1011 WMPropList *WMRetainPropList(WMPropList * plist)
1013 WMPropList *key, *value;
1014 WMHashEnumerator e;
1015 int i;
1017 plist->retainCount++;
1019 switch (plist->type) {
1020 case WPLString:
1021 case WPLData:
1022 break;
1023 case WPLArray:
1024 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1025 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1027 break;
1028 case WPLDictionary:
1029 e = WMEnumerateHashTable(plist->d.dict);
1030 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1031 WMRetainPropList(key);
1032 WMRetainPropList(value);
1034 break;
1035 default:
1036 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1037 wassertrv(False, NULL);
1038 break;
1041 return plist;
1044 void WMReleasePropList(WMPropList * plist)
1046 WMPropList *key, *value;
1047 WMHashEnumerator e;
1048 int i;
1050 plist->retainCount--;
1052 switch (plist->type) {
1053 case WPLString:
1054 if (plist->retainCount < 1) {
1055 wfree(plist->d.string);
1056 wfree(plist);
1058 break;
1059 case WPLData:
1060 if (plist->retainCount < 1) {
1061 WMReleaseData(plist->d.data);
1062 wfree(plist);
1064 break;
1065 case WPLArray:
1066 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1067 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1069 if (plist->retainCount < 1) {
1070 WMFreeArray(plist->d.array);
1071 wfree(plist);
1073 break;
1074 case WPLDictionary:
1075 e = WMEnumerateHashTable(plist->d.dict);
1076 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1077 WMReleasePropList(key);
1078 WMReleasePropList(value);
1080 if (plist->retainCount < 1) {
1081 WMFreeHashTable(plist->d.dict);
1082 wfree(plist);
1084 break;
1085 default:
1086 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1087 wassertr(False);
1088 break;
1092 void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
1094 wassertr(plist->type == WPLArray);
1096 retainPropListByCount(item, plist->retainCount);
1097 WMInsertInArray(plist->d.array, index, item);
1100 void WMAddToPLArray(WMPropList * plist, WMPropList * item)
1102 wassertr(plist->type == WPLArray);
1104 retainPropListByCount(item, plist->retainCount);
1105 WMAddToArray(plist->d.array, item);
1108 void WMDeleteFromPLArray(WMPropList * plist, int index)
1110 WMPropList *item;
1112 wassertr(plist->type == WPLArray);
1114 item = WMGetFromArray(plist->d.array, index);
1115 if (item != NULL) {
1116 WMDeleteFromArray(plist->d.array, index);
1117 releasePropListByCount(item, plist->retainCount);
1121 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1123 WMPropList *iPtr;
1124 int i;
1126 wassertr(plist->type == WPLArray);
1128 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1129 iPtr = WMGetFromArray(plist->d.array, i);
1130 if (WMIsPropListEqualTo(item, iPtr)) {
1131 WMDeleteFromArray(plist->d.array, i);
1132 releasePropListByCount(iPtr, plist->retainCount);
1133 break;
1138 void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
1140 wassertr(plist->type == WPLDictionary);
1142 /*WMRetainPropList(key); */
1143 WMRemoveFromPLDictionary(plist, key);
1144 retainPropListByCount(key, plist->retainCount);
1145 retainPropListByCount(value, plist->retainCount);
1146 WMHashInsert(plist->d.dict, key, value);
1147 /*WMReleasePropList(key); */
1150 void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
1152 WMPropList *k, *v;
1154 wassertr(plist->type == WPLDictionary);
1156 if (WMHashGetItemAndKey(plist->d.dict, key, (void **)&v, (void **)&k)) {
1157 WMHashRemove(plist->d.dict, k);
1158 releasePropListByCount(k, plist->retainCount);
1159 releasePropListByCount(v, plist->retainCount);
1163 WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1165 WMPropList *key, *value, *dvalue;
1166 WMHashEnumerator e;
1168 wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1170 if (source == dest)
1171 return dest;
1173 e = WMEnumerateHashTable(source->d.dict);
1174 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1175 if (recursive && value->type == WPLDictionary) {
1176 dvalue = WMHashGet(dest->d.dict, key);
1177 if (dvalue && dvalue->type == WPLDictionary) {
1178 WMMergePLDictionaries(dvalue, value, True);
1179 } else {
1180 WMPutInPLDictionary(dest, key, value);
1182 } else {
1183 WMPutInPLDictionary(dest, key, value);
1187 return dest;
1190 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1192 WMPropList *key, *value, *dvalue;
1193 WMHashEnumerator e;
1195 wassertr(source->type == WPLDictionary && dest->type == WPLDictionary);
1197 if (source == dest) {
1198 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1199 int i;
1201 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1202 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1204 return dest;
1207 e = WMEnumerateHashTable(source->d.dict);
1208 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1209 dvalue = WMHashGet(dest->d.dict, key);
1210 if (!dvalue)
1211 continue;
1212 if (WMIsPropListEqualTo(value, dvalue)) {
1213 WMRemoveFromPLDictionary(dest, key);
1214 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1215 WMSubtractPLDictionaries(dvalue, value, True);
1219 return dest;
1222 int WMGetPropListItemCount(WMPropList * plist)
1224 switch (plist->type) {
1225 case WPLString:
1226 case WPLData:
1227 return 0; /* should this be 1 instead? */
1228 case WPLArray:
1229 return WMGetArrayItemCount(plist->d.array);
1230 case WPLDictionary:
1231 return (int)WMCountHashTable(plist->d.dict);
1232 default:
1233 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1234 wassertrv(False, 0);
1235 break;
1238 return 0;
1241 Bool WMIsPLString(WMPropList * plist)
1243 return (plist->type == WPLString);
1246 Bool WMIsPLData(WMPropList * plist)
1248 return (plist->type == WPLData);
1251 Bool WMIsPLArray(WMPropList * plist)
1253 return (plist->type == WPLArray);
1256 Bool WMIsPLDictionary(WMPropList * plist)
1258 return (plist->type == WPLDictionary);
1261 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1263 WMPropList *key1, *item1, *item2;
1264 WMHashEnumerator enumerator;
1265 int n, i;
1267 if (plist->type != other->type)
1268 return False;
1270 switch (plist->type) {
1271 case WPLString:
1272 if (caseSensitive) {
1273 return (strcmp(plist->d.string, other->d.string) == 0);
1274 } else {
1275 return (strcasecmp(plist->d.string, other->d.string) == 0);
1277 case WPLData:
1278 return WMIsDataEqualToData(plist->d.data, other->d.data);
1279 case WPLArray:
1280 n = WMGetArrayItemCount(plist->d.array);
1281 if (n != WMGetArrayItemCount(other->d.array))
1282 return False;
1283 for (i = 0; i < n; i++) {
1284 item1 = WMGetFromArray(plist->d.array, i);
1285 item2 = WMGetFromArray(other->d.array, i);
1286 if (!WMIsPropListEqualTo(item1, item2))
1287 return False;
1289 return True;
1290 case WPLDictionary:
1291 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1292 return False;
1293 enumerator = WMEnumerateHashTable(plist->d.dict);
1294 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1295 item2 = WMHashGet(other->d.dict, key1);
1296 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1297 return False;
1299 return True;
1300 default:
1301 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1302 wassertrv(False, False);
1303 break;
1306 return False;
1309 char *WMGetFromPLString(WMPropList * plist)
1311 wassertrv(plist->type == WPLString, NULL);
1313 return plist->d.string;
1316 WMData *WMGetFromPLData(WMPropList * plist)
1318 wassertrv(plist->type == WPLData, NULL);
1320 return plist->d.data;
1323 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1325 wassertrv(plist->type == WPLData, NULL);
1327 return WMDataBytes(plist->d.data);
1330 int WMGetPLDataLength(WMPropList * plist)
1332 wassertrv(plist->type == WPLData, 0);
1334 return WMGetDataLength(plist->d.data);
1337 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1339 wassertrv(plist->type == WPLArray, NULL);
1341 return WMGetFromArray(plist->d.array, index);
1344 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1346 wassertrv(plist->type == WPLDictionary, NULL);
1348 return WMHashGet(plist->d.dict, key);
1351 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1353 WMPropList *array, *key;
1354 WMHashEnumerator enumerator;
1356 wassertrv(plist->type == WPLDictionary, NULL);
1358 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1359 array->type = WPLArray;
1360 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1361 array->retainCount = 1;
1363 enumerator = WMEnumerateHashTable(plist->d.dict);
1364 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1365 WMAddToArray(array->d.array, WMRetainPropList(key));
1368 return array;
1371 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1373 WMPropList *ret = NULL;
1374 WMPropList *key, *item;
1375 WMHashEnumerator e;
1376 WMData *data;
1377 int i;
1379 switch (plist->type) {
1380 case WPLString:
1381 ret = WMCreatePLString(plist->d.string);
1382 break;
1383 case WPLData:
1384 data = WMCreateDataWithData(plist->d.data);
1385 ret = WMCreatePLData(data);
1386 WMReleaseData(data);
1387 break;
1388 case WPLArray:
1389 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1390 ret->type = WPLArray;
1391 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1392 ret->retainCount = 1;
1394 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1395 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1397 break;
1398 case WPLDictionary:
1399 ret = WMCreatePLDictionary(NULL, NULL);
1400 e = WMEnumerateHashTable(plist->d.dict);
1401 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1402 WMPutInPLDictionary(ret, key, item);
1404 break;
1405 default:
1406 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1407 wassertrv(False, NULL);
1408 break;
1411 return ret;
1414 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1416 WMPropList *ret = NULL;
1417 WMPropList *key, *item;
1418 WMHashEnumerator e;
1419 WMData *data;
1420 int i;
1422 switch (plist->type) {
1423 case WPLString:
1424 ret = WMCreatePLString(plist->d.string);
1425 break;
1426 case WPLData:
1427 data = WMCreateDataWithData(plist->d.data);
1428 ret = WMCreatePLData(data);
1429 WMReleaseData(data);
1430 break;
1431 case WPLArray:
1432 ret = WMCreatePLArray(NULL);
1433 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1434 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1435 WMAddToArray(ret->d.array, item);
1437 break;
1438 case WPLDictionary:
1439 ret = WMCreatePLDictionary(NULL, NULL);
1440 e = WMEnumerateHashTable(plist->d.dict);
1441 /* While we copy an existing dictionary there is no way that we can
1442 * have duplicate keys, so we don't need to first remove a key/value
1443 * pair before inserting the new key/value.
1445 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1446 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1448 break;
1449 default:
1450 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1451 wassertrv(False, NULL);
1452 break;
1455 return ret;
1458 WMPropList *WMCreatePropListFromDescription(char *desc)
1460 WMPropList *plist = NULL;
1461 PLData *pldata;
1463 pldata = (PLData *) wmalloc(sizeof(PLData));
1464 memset(pldata, 0, sizeof(PLData));
1465 pldata->ptr = desc;
1466 pldata->lineNumber = 1;
1468 plist = getPropList(pldata);
1470 if (getNonSpaceChar(pldata) != 0 && plist) {
1471 COMPLAIN(pldata, _("extra data after end of property list"));
1473 * We can't just ignore garbage after the end of the description
1474 * (especially if the description was read from a file), because
1475 * the "garbage" can be the real data and the real garbage is in
1476 * fact in the beginning of the file (which is now inside plist)
1478 WMReleasePropList(plist);
1479 plist = NULL;
1482 wfree(pldata);
1484 return plist;
1487 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1489 return (indented ? indentedDescription(plist, 0) : description(plist));
1492 WMPropList *WMReadPropListFromFile(char *file)
1494 WMPropList *plist = NULL;
1495 PLData *pldata;
1496 FILE *f;
1497 struct stat stbuf;
1498 size_t length;
1500 f = fopen(file, "rb");
1501 if (!f) {
1502 /* let the user print the error message if he really needs to */
1503 /*wsyserror(_("could not open domain file '%s' for reading"), file); */
1504 return NULL;
1507 if (stat(file, &stbuf) == 0) {
1508 length = (size_t) stbuf.st_size;
1509 } else {
1510 wsyserror(_("could not get size for file '%s'"), file);
1511 fclose(f);
1512 return NULL;
1515 pldata = (PLData *) wmalloc(sizeof(PLData));
1516 memset(pldata, 0, sizeof(PLData));
1517 pldata->ptr = (char *)wmalloc(length + 1);
1518 pldata->filename = file;
1519 pldata->lineNumber = 1;
1521 if (fread(pldata->ptr, length, 1, f) != 1) {
1522 if (ferror(f)) {
1523 wsyserror(_("error reading from file '%s'"), file);
1525 plist = NULL;
1526 goto cleanup;
1529 pldata->ptr[length] = 0;
1531 plist = getPropList(pldata);
1533 if (getNonSpaceChar(pldata) != 0 && plist) {
1534 COMPLAIN(pldata, _("extra data after end of property list"));
1536 * We can't just ignore garbage after the end of the description
1537 * (especially if the description was read from a file), because
1538 * the "garbage" can be the real data and the real garbage is in
1539 * fact in the beginning of the file (which is now inside plist)
1541 WMReleasePropList(plist);
1542 plist = NULL;
1545 cleanup:
1546 wfree(pldata->ptr);
1547 wfree(pldata);
1548 fclose(f);
1550 return plist;
1553 /* TODO: review this function's code */
1555 Bool WMWritePropListToFile(WMPropList * plist, char *path)
1557 char *thePath = NULL;
1558 char *desc;
1559 FILE *theFile;
1560 #ifdef HAVE_MKSTEMP
1561 int fd, mask;
1562 #endif
1564 if (!wmkdirhier(path))
1565 return False;
1567 /* Use the path name of the destination file as a prefix for the
1568 * mkstemp() call so that we can be sure that both files are on
1569 * the same filesystem and the subsequent rename() will work. */
1570 thePath = wstrconcat(path, ".XXXXXX");
1572 #ifdef HAVE_MKSTEMP
1573 if ((fd = mkstemp(thePath)) < 0) {
1574 wsyserror(_("mkstemp (%s) failed"), thePath);
1575 goto failure;
1577 mask = umask(0);
1578 umask(mask);
1579 fchmod(fd, 0644 & ~mask);
1580 if ((theFile = fdopen(fd, "wb")) == NULL) {
1581 close(fd);
1583 #else
1584 if (mktemp(thePath) == NULL) {
1585 wsyserror(_("mktemp (%s) failed"), thePath);
1586 goto failure;
1588 theFile = fopen(thePath, "wb");
1589 #endif
1591 if (theFile == NULL) {
1592 wsyserror(_("open (%s) failed"), thePath);
1593 goto failure;
1596 desc = indentedDescription(plist, 0);
1598 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1599 wsyserror(_("writing to file: %s failed"), thePath);
1600 wfree(desc);
1601 goto failure;
1604 wfree(desc);
1606 (void)fsync(fileno(theFile));
1607 if (fclose(theFile) != 0) {
1608 wsyserror(_("fclose (%s) failed"), thePath);
1609 goto failure;
1612 /* If we used a temporary file, we still need to rename() it be the
1613 * real file. Also, we need to try to retain the file attributes of
1614 * the original file we are overwriting (if we are) */
1615 if (rename(thePath, path) != 0) {
1616 wsyserror(_("rename ('%s' to '%s') failed"), thePath, path);
1617 goto failure;
1620 wfree(thePath);
1621 return True;
1623 failure:
1624 unlink(thePath);
1625 wfree(thePath);
1626 return False;
1630 * create a directory hierarchy
1632 * if the last octet of `path' is `/', the full path is
1633 * assumed to be a directory; otherwise path is assumed to be a
1634 * file, and the last component is stripped off. the rest is the
1635 * the hierarchy to be created.
1637 * refuses to create anything outside $GNUSTEP_USER_ROOT
1639 * returns 1 on success, 0 on failure
1641 int wmkdirhier(const char *path)
1643 char *t, *thePath = NULL, buf[1024];
1644 size_t p, plen;
1645 struct stat st;
1647 /* Only create directories under $GNUSTEP_USER_ROOT */
1648 if ((t = wusergnusteppath()) == NULL)
1649 return 0;
1650 if (strncmp(path, t, strlen(t)) != 0)
1651 return 0;
1653 thePath = wstrdup(path);
1654 /* Strip the trailing component if it is a file */
1655 p = strlen(thePath);
1656 while (p && thePath[p] != '/')
1657 thePath[p--] = '\0';
1659 thePath[p] = '\0';
1661 /* Shortcut if it already exists */
1662 if (stat(thePath, &st) == 0) {
1663 wfree(thePath);
1664 if (S_ISDIR(st.st_mode)) {
1665 /* Is a directory alright */
1666 return 1;
1667 } else {
1668 /* Exists, but not a directory, the caller
1669 * might just as well abort now */
1670 return 0;
1674 memset(buf, 0, sizeof(buf));
1675 strncpy(buf, t, sizeof(buf) - 1);
1676 p = strlen(buf);
1677 plen = strlen(thePath);
1679 do {
1680 while (p++ < plen && thePath[p] != '/')
1683 strncpy(buf, thePath, p);
1684 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1685 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1686 wsyserror(_("Could not create component %s"), buf);
1687 wfree(thePath);
1688 return 0;
1690 } while (p < plen);
1692 wfree(thePath);
1693 return 1;
1697 * remove a directory hierarchy
1699 * refuses to remove anything outside $GNUSTEP_USER_ROOT
1701 * returns 1 on success, 0 on failure
1703 * TODO: revisit what's error and what's not
1705 * with inspirations from OpenBSD's bin/rm/rm.c
1707 int wrmdirhier(const char *path)
1709 FTS *fts;
1710 FTSENT *p;
1711 char *t;
1712 struct stat st;
1713 char *ptree[2];
1715 /* Only remove directories under $GNUSTEP_USER_ROOT */
1716 if ((t = wusergnusteppath()) == NULL)
1717 return 0;
1718 if (strncmp(path, t, strlen(t)) != 0)
1719 return 0;
1721 /* Shortcut if it doesn't exist to begin with */
1722 if (stat(path, &st) == -1)
1723 return 0;
1725 ptree[0] = (char *)path;
1726 ptree[1] = NULL;
1728 if (!(fts = fts_open(ptree, FTS_PHYSICAL, NULL)))
1729 return 0;
1731 while ((p = fts_read(fts)) != NULL) {
1732 switch(p->fts_info) {
1733 case FTS_D:
1734 continue;
1735 break;
1736 case FTS_DP:
1737 rmdir(p->fts_path);
1738 continue;
1739 break;
1740 case FTS_F:
1741 unlink(p->fts_path);
1742 continue;
1743 break;
1744 default: continue;