WINGs: Added 'const' attribute to 'WMCreatePropListFromDescription'
[wmaker-crm.git] / WINGs / proplist.c
blob801206e4d9eca655c789156d7a8298d98a42b14c
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 const char *ptr;
40 int pos;
41 const 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(const char *src)
507 char *dest = wmalloc(strlen(src) + 1);
508 char *dPtr;
509 char ch;
511 for (dPtr = dest; ; dPtr++) {
512 ch = *src++;
513 if (ch == '\0')
514 break;
515 else if (ch != '\\')
516 *dPtr = ch;
517 else {
518 ch = *(src++);
519 if (ch == '\0') {
520 *dPtr = '\\';
521 break;
522 } else if ((ch >= '0') && (ch <= '7')) {
523 char wch;
525 /* Convert octal number to character */
526 wch = (ch & 07);
527 ch = *src;
528 if ((ch >= '0') && (ch <= '7')) {
529 src++;
530 wch = (wch << 3) | (ch & 07);
531 ch = *src;
532 if ((ch >= '0') && (ch <= '7')) {
533 src++;
534 wch = (wch << 3) | (ch & 07);
537 *dPtr = wch;
538 } else {
539 switch (ch) {
540 case 'a':
541 *dPtr = '\a';
542 break;
543 case 'b':
544 *dPtr = '\b';
545 break;
546 case 't':
547 *dPtr = '\t';
548 break;
549 case 'r':
550 *dPtr = '\r';
551 break;
552 case 'n':
553 *dPtr = '\n';
554 break;
555 case 'v':
556 *dPtr = '\v';
557 break;
558 case 'f':
559 *dPtr = '\f';
560 break;
561 default:
562 *dPtr = ch;
568 *dPtr = 0;
570 return dest;
573 static WMPropList *getPLString(PLData * pldata)
575 WMPropList *plist;
576 StringBuffer sBuf;
577 int ptr = 0;
578 int c;
580 sBuf.str = wmalloc(BUFFERSIZE);
581 sBuf.size = BUFFERSIZE;
583 while (1) {
584 c = getChar(pldata);
585 if (ISSTRINGABLE(c)) {
586 CHECK_BUFFER_SIZE(sBuf, ptr);
587 sBuf.str[ptr++] = c;
588 } else {
589 if (c != 0) {
590 pldata->pos--;
592 break;
596 sBuf.str[ptr] = 0;
598 if (ptr == 0) {
599 plist = NULL;
600 } else {
601 char *tmp = unescapestr(sBuf.str);
602 plist = WMCreatePLString(tmp);
603 wfree(tmp);
606 wfree(sBuf.str);
608 return plist;
611 static WMPropList *getPLQString(PLData * pldata)
613 WMPropList *plist;
614 int ptr = 0, escaping = 0, ok = 1;
615 int c;
616 StringBuffer sBuf;
618 sBuf.str = wmalloc(BUFFERSIZE);
619 sBuf.size = BUFFERSIZE;
621 while (1) {
622 c = getChar(pldata);
623 if (!escaping) {
624 if (c == '\\') {
625 escaping = 1;
626 continue;
627 } else if (c == '"') {
628 break;
630 } else {
631 CHECK_BUFFER_SIZE(sBuf, ptr);
632 sBuf.str[ptr++] = '\\';
633 escaping = 0;
636 if (c == 0) {
637 COMPLAIN(pldata, _("unterminated PropList string"));
638 ok = 0;
639 break;
640 } else {
641 CHECK_BUFFER_SIZE(sBuf, ptr);
642 sBuf.str[ptr++] = c;
646 sBuf.str[ptr] = 0;
648 if (!ok) {
649 plist = NULL;
650 } else {
651 char *tmp = unescapestr(sBuf.str);
652 plist = WMCreatePLString(tmp);
653 wfree(tmp);
656 wfree(sBuf.str);
658 return plist;
661 static WMPropList *getPLData(PLData * pldata)
663 int ok = 1;
664 int len = 0;
665 int c1, c2;
666 unsigned char buf[BUFFERSIZE], byte;
667 WMPropList *plist;
668 WMData *data;
670 data = WMCreateDataWithCapacity(0);
672 while (1) {
673 c1 = getNonSpaceChar(pldata);
674 if (c1 == 0) {
675 COMPLAIN(pldata, _("unterminated PropList data"));
676 ok = 0;
677 break;
678 } else if (c1 == '>') {
679 break;
680 } else if (ishexdigit(c1)) {
681 c2 = getNonSpaceChar(pldata);
682 if (c2 == 0 || c2 == '>') {
683 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
684 ok = 0;
685 break;
686 } else if (ishexdigit(c2)) {
687 byte = char2num(c1) << 4;
688 byte |= char2num(c2);
689 buf[len++] = byte;
690 if (len == sizeof(buf)) {
691 WMAppendDataBytes(data, buf, len);
692 len = 0;
694 } else {
695 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
696 ok = 0;
697 break;
699 } else {
700 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
701 ok = 0;
702 break;
706 if (!ok) {
707 WMReleaseData(data);
708 return NULL;
711 if (len > 0)
712 WMAppendDataBytes(data, buf, len);
714 plist = WMCreatePLData(data);
715 WMReleaseData(data);
717 return plist;
720 static WMPropList *getPLArray(PLData * pldata)
722 Bool first = True;
723 int ok = 1;
724 int c;
725 WMPropList *array, *obj;
727 array = WMCreatePLArray(NULL);
729 while (1) {
730 c = getNonSpaceChar(pldata);
731 if (c == 0) {
732 COMPLAIN(pldata, _("unterminated PropList array"));
733 ok = 0;
734 break;
735 } else if (c == ')') {
736 break;
737 } else if (c == ',') {
738 /* continue normally */
739 } else if (!first) {
740 COMPLAIN(pldata, _("missing or unterminated PropList array"));
741 ok = 0;
742 break;
743 } else {
744 pldata->pos--;
746 first = False;
748 obj = getPropList(pldata);
749 if (!obj) {
750 COMPLAIN(pldata, _("could not get PropList array element"));
751 ok = 0;
752 break;
754 WMAddToPLArray(array, obj);
755 WMReleasePropList(obj);
758 if (!ok) {
759 WMReleasePropList(array);
760 array = NULL;
763 return array;
766 static WMPropList *getPLDictionary(PLData * pldata)
768 int ok = 1;
769 int c;
770 WMPropList *dict, *key, *value;
772 dict = WMCreatePLDictionary(NULL, NULL);
774 while (1) {
775 c = getNonSpaceChar(pldata);
776 if (c == 0) {
777 COMPLAIN(pldata, _("unterminated PropList dictionary"));
778 ok = 0;
779 break;
780 } else if (c == '}') {
781 break;
784 DPUT("getting PropList dictionary key");
785 if (c == '<') {
786 key = getPLData(pldata);
787 } else if (c == '"') {
788 key = getPLQString(pldata);
789 } else if (ISSTRINGABLE(c)) {
790 pldata->pos--;
791 key = getPLString(pldata);
792 } else {
793 if (c == '=') {
794 COMPLAIN(pldata, _("missing PropList dictionary key"));
795 } else {
796 COMPLAIN(pldata, _("missing PropList dictionary entry key "
797 "or unterminated dictionary"));
799 ok = 0;
800 break;
803 if (!key) {
804 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
805 ok = 0;
806 break;
809 c = getNonSpaceChar(pldata);
810 if (c != '=') {
811 WMReleasePropList(key);
812 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
813 ok = 0;
814 break;
817 DPUT("getting PropList dictionary entry value for key");
818 value = getPropList(pldata);
819 if (!value) {
820 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
821 WMReleasePropList(key);
822 ok = 0;
823 break;
826 c = getNonSpaceChar(pldata);
827 if (c != ';') {
828 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
829 WMReleasePropList(key);
830 WMReleasePropList(value);
831 ok = 0;
832 break;
835 WMPutInPLDictionary(dict, key, value);
836 WMReleasePropList(key);
837 WMReleasePropList(value);
840 if (!ok) {
841 WMReleasePropList(dict);
842 dict = NULL;
845 return dict;
848 static WMPropList *getPropList(PLData * pldata)
850 WMPropList *plist;
851 int c;
853 c = getNonSpaceChar(pldata);
855 switch (c) {
856 case 0:
857 DPUT("End of PropList");
858 plist = NULL;
859 break;
861 case '{':
862 DPUT("Getting PropList dictionary");
863 plist = getPLDictionary(pldata);
864 break;
866 case '(':
867 DPUT("Getting PropList array");
868 plist = getPLArray(pldata);
869 break;
871 case '<':
872 DPUT("Getting PropList data");
873 plist = getPLData(pldata);
874 break;
876 case '"':
877 DPUT("Getting PropList quoted string");
878 plist = getPLQString(pldata);
879 break;
881 default:
882 if (ISSTRINGABLE(c)) {
883 DPUT("Getting PropList string");
884 pldata->pos--;
885 plist = getPLString(pldata);
886 } else {
887 COMPLAIN(pldata, _("was expecting a string, data, array or "
888 "dictionary. If it's a string, try enclosing " "it with \"."));
889 if (c == '#' || c == '/') {
890 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
892 plist = NULL;
894 break;
897 return plist;
900 void WMPLSetCaseSensitive(Bool caseSensitiveness)
902 caseSensitive = caseSensitiveness;
905 WMPropList *WMCreatePLString(const char *str)
907 WMPropList *plist;
909 wassertrv(str != NULL, NULL);
911 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
912 plist->type = WPLString;
913 plist->d.string = wstrdup(str);
914 plist->retainCount = 1;
916 return plist;
919 WMPropList *WMCreatePLData(WMData * data)
921 WMPropList *plist;
923 wassertrv(data != NULL, NULL);
925 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
926 plist->type = WPLData;
927 plist->d.data = WMRetainData(data);
928 plist->retainCount = 1;
930 return plist;
933 WMPropList *WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int length)
935 WMPropList *plist;
937 wassertrv(bytes != NULL, NULL);
939 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
940 plist->type = WPLData;
941 plist->d.data = WMCreateDataWithBytes(bytes, length);
942 plist->retainCount = 1;
944 return plist;
947 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
949 WMPropList *plist;
951 wassertrv(bytes != NULL, NULL);
953 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
954 plist->type = WPLData;
955 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
956 plist->retainCount = 1;
958 return plist;
961 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
963 WMPropList *plist, *nelem;
964 va_list ap;
966 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
967 plist->type = WPLArray;
968 plist->d.array = WMCreateArray(4);
969 plist->retainCount = 1;
971 if (!elem)
972 return plist;
974 WMAddToArray(plist->d.array, WMRetainPropList(elem));
976 va_start(ap, elem);
978 while (1) {
979 nelem = va_arg(ap, WMPropList *);
980 if (!nelem) {
981 va_end(ap);
982 return plist;
984 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
988 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
990 WMPropList *plist, *nkey, *nvalue, *k, *v;
991 va_list ap;
993 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
994 plist->type = WPLDictionary;
995 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
996 plist->retainCount = 1;
998 if (!key || !value)
999 return plist;
1001 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
1003 va_start(ap, value);
1005 while (1) {
1006 nkey = va_arg(ap, WMPropList *);
1007 if (!nkey) {
1008 va_end(ap);
1009 return plist;
1011 nvalue = va_arg(ap, WMPropList *);
1012 if (!nvalue) {
1013 va_end(ap);
1014 return plist;
1016 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
1017 WMHashRemove(plist->d.dict, k);
1018 WMReleasePropList(k);
1019 WMReleasePropList(v);
1021 WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
1025 WMPropList *WMRetainPropList(WMPropList * plist)
1027 WMPropList *key, *value;
1028 WMHashEnumerator e;
1029 int i;
1031 plist->retainCount++;
1033 switch (plist->type) {
1034 case WPLString:
1035 case WPLData:
1036 break;
1037 case WPLArray:
1038 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1039 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1041 break;
1042 case WPLDictionary:
1043 e = WMEnumerateHashTable(plist->d.dict);
1044 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1045 WMRetainPropList(key);
1046 WMRetainPropList(value);
1048 break;
1049 default:
1050 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1051 wassertrv(False, NULL);
1052 break;
1055 return plist;
1058 void WMReleasePropList(WMPropList * plist)
1060 WMPropList *key, *value;
1061 WMHashEnumerator e;
1062 int i;
1064 plist->retainCount--;
1066 switch (plist->type) {
1067 case WPLString:
1068 if (plist->retainCount < 1) {
1069 wfree(plist->d.string);
1070 wfree(plist);
1072 break;
1073 case WPLData:
1074 if (plist->retainCount < 1) {
1075 WMReleaseData(plist->d.data);
1076 wfree(plist);
1078 break;
1079 case WPLArray:
1080 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1081 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1083 if (plist->retainCount < 1) {
1084 WMFreeArray(plist->d.array);
1085 wfree(plist);
1087 break;
1088 case WPLDictionary:
1089 e = WMEnumerateHashTable(plist->d.dict);
1090 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1091 WMReleasePropList(key);
1092 WMReleasePropList(value);
1094 if (plist->retainCount < 1) {
1095 WMFreeHashTable(plist->d.dict);
1096 wfree(plist);
1098 break;
1099 default:
1100 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1101 wassertr(False);
1102 break;
1106 void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
1108 wassertr(plist->type == WPLArray);
1110 retainPropListByCount(item, plist->retainCount);
1111 WMInsertInArray(plist->d.array, index, item);
1114 void WMAddToPLArray(WMPropList * plist, WMPropList * item)
1116 wassertr(plist->type == WPLArray);
1118 retainPropListByCount(item, plist->retainCount);
1119 WMAddToArray(plist->d.array, item);
1122 void WMDeleteFromPLArray(WMPropList * plist, int index)
1124 WMPropList *item;
1126 wassertr(plist->type == WPLArray);
1128 item = WMGetFromArray(plist->d.array, index);
1129 if (item != NULL) {
1130 WMDeleteFromArray(plist->d.array, index);
1131 releasePropListByCount(item, plist->retainCount);
1135 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1137 WMPropList *iPtr;
1138 int i;
1140 wassertr(plist->type == WPLArray);
1142 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1143 iPtr = WMGetFromArray(plist->d.array, i);
1144 if (WMIsPropListEqualTo(item, iPtr)) {
1145 WMDeleteFromArray(plist->d.array, i);
1146 releasePropListByCount(iPtr, plist->retainCount);
1147 break;
1152 void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
1154 wassertr(plist->type == WPLDictionary);
1156 /*WMRetainPropList(key); */
1157 WMRemoveFromPLDictionary(plist, key);
1158 retainPropListByCount(key, plist->retainCount);
1159 retainPropListByCount(value, plist->retainCount);
1160 WMHashInsert(plist->d.dict, key, value);
1161 /*WMReleasePropList(key); */
1164 void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
1166 WMPropList *k, *v;
1168 wassertr(plist->type == WPLDictionary);
1170 if (WMHashGetItemAndKey(plist->d.dict, key, (void **)&v, (void **)&k)) {
1171 WMHashRemove(plist->d.dict, k);
1172 releasePropListByCount(k, plist->retainCount);
1173 releasePropListByCount(v, plist->retainCount);
1177 WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1179 WMPropList *key, *value, *dvalue;
1180 WMHashEnumerator e;
1182 wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
1184 if (source == dest)
1185 return dest;
1187 e = WMEnumerateHashTable(source->d.dict);
1188 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1189 if (recursive && value->type == WPLDictionary) {
1190 dvalue = WMHashGet(dest->d.dict, key);
1191 if (dvalue && dvalue->type == WPLDictionary) {
1192 WMMergePLDictionaries(dvalue, value, True);
1193 } else {
1194 WMPutInPLDictionary(dest, key, value);
1196 } else {
1197 WMPutInPLDictionary(dest, key, value);
1201 return dest;
1204 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1206 WMPropList *key, *value, *dvalue;
1207 WMHashEnumerator e;
1209 wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
1211 if (source == dest) {
1212 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1213 int i;
1215 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1216 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1218 return dest;
1221 e = WMEnumerateHashTable(source->d.dict);
1222 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1223 dvalue = WMHashGet(dest->d.dict, key);
1224 if (!dvalue)
1225 continue;
1226 if (WMIsPropListEqualTo(value, dvalue)) {
1227 WMRemoveFromPLDictionary(dest, key);
1228 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1229 WMSubtractPLDictionaries(dvalue, value, True);
1233 return dest;
1236 int WMGetPropListItemCount(WMPropList * plist)
1238 switch (plist->type) {
1239 case WPLString:
1240 case WPLData:
1241 return 0; /* should this be 1 instead? */
1242 case WPLArray:
1243 return WMGetArrayItemCount(plist->d.array);
1244 case WPLDictionary:
1245 return (int)WMCountHashTable(plist->d.dict);
1246 default:
1247 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1248 wassertrv(False, 0);
1249 break;
1252 return 0;
1255 Bool WMIsPLString(WMPropList * plist)
1257 return (plist->type == WPLString);
1260 Bool WMIsPLData(WMPropList * plist)
1262 return (plist->type == WPLData);
1265 Bool WMIsPLArray(WMPropList * plist)
1267 return (plist->type == WPLArray);
1270 Bool WMIsPLDictionary(WMPropList * plist)
1272 return (plist->type == WPLDictionary);
1275 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1277 WMPropList *key1, *item1, *item2;
1278 WMHashEnumerator enumerator;
1279 int n, i;
1281 if (plist->type != other->type)
1282 return False;
1284 switch (plist->type) {
1285 case WPLString:
1286 if (caseSensitive) {
1287 return (strcmp(plist->d.string, other->d.string) == 0);
1288 } else {
1289 return (strcasecmp(plist->d.string, other->d.string) == 0);
1291 case WPLData:
1292 return WMIsDataEqualToData(plist->d.data, other->d.data);
1293 case WPLArray:
1294 n = WMGetArrayItemCount(plist->d.array);
1295 if (n != WMGetArrayItemCount(other->d.array))
1296 return False;
1297 for (i = 0; i < n; i++) {
1298 item1 = WMGetFromArray(plist->d.array, i);
1299 item2 = WMGetFromArray(other->d.array, i);
1300 if (!WMIsPropListEqualTo(item1, item2))
1301 return False;
1303 return True;
1304 case WPLDictionary:
1305 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1306 return False;
1307 enumerator = WMEnumerateHashTable(plist->d.dict);
1308 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1309 item2 = WMHashGet(other->d.dict, key1);
1310 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1311 return False;
1313 return True;
1314 default:
1315 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1316 wassertrv(False, False);
1317 break;
1320 return False;
1323 char *WMGetFromPLString(WMPropList * plist)
1325 wassertrv(plist->type == WPLString, NULL);
1327 return plist->d.string;
1330 WMData *WMGetFromPLData(WMPropList * plist)
1332 wassertrv(plist->type == WPLData, NULL);
1334 return plist->d.data;
1337 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1339 wassertrv(plist->type == WPLData, NULL);
1341 return WMDataBytes(plist->d.data);
1344 int WMGetPLDataLength(WMPropList * plist)
1346 wassertrv(plist->type == WPLData, 0);
1348 return WMGetDataLength(plist->d.data);
1351 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1353 wassertrv(plist->type == WPLArray, NULL);
1355 return WMGetFromArray(plist->d.array, index);
1358 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1360 wassertrv(plist->type == WPLDictionary, NULL);
1362 return WMHashGet(plist->d.dict, key);
1365 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1367 WMPropList *array, *key;
1368 WMHashEnumerator enumerator;
1370 wassertrv(plist->type == WPLDictionary, NULL);
1372 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1373 array->type = WPLArray;
1374 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1375 array->retainCount = 1;
1377 enumerator = WMEnumerateHashTable(plist->d.dict);
1378 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1379 WMAddToArray(array->d.array, WMRetainPropList(key));
1382 return array;
1385 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1387 WMPropList *ret = NULL;
1388 WMPropList *key, *item;
1389 WMHashEnumerator e;
1390 WMData *data;
1391 int i;
1393 switch (plist->type) {
1394 case WPLString:
1395 ret = WMCreatePLString(plist->d.string);
1396 break;
1397 case WPLData:
1398 data = WMCreateDataWithData(plist->d.data);
1399 ret = WMCreatePLData(data);
1400 WMReleaseData(data);
1401 break;
1402 case WPLArray:
1403 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1404 ret->type = WPLArray;
1405 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1406 ret->retainCount = 1;
1408 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1409 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1411 break;
1412 case WPLDictionary:
1413 ret = WMCreatePLDictionary(NULL, NULL);
1414 e = WMEnumerateHashTable(plist->d.dict);
1415 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1416 WMPutInPLDictionary(ret, key, item);
1418 break;
1419 default:
1420 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1421 wassertrv(False, NULL);
1422 break;
1425 return ret;
1428 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1430 WMPropList *ret = NULL;
1431 WMPropList *key, *item;
1432 WMHashEnumerator e;
1433 WMData *data;
1434 int i;
1436 switch (plist->type) {
1437 case WPLString:
1438 ret = WMCreatePLString(plist->d.string);
1439 break;
1440 case WPLData:
1441 data = WMCreateDataWithData(plist->d.data);
1442 ret = WMCreatePLData(data);
1443 WMReleaseData(data);
1444 break;
1445 case WPLArray:
1446 ret = WMCreatePLArray(NULL);
1447 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1448 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1449 WMAddToArray(ret->d.array, item);
1451 break;
1452 case WPLDictionary:
1453 ret = WMCreatePLDictionary(NULL, NULL);
1454 e = WMEnumerateHashTable(plist->d.dict);
1455 /* While we copy an existing dictionary there is no way that we can
1456 * have duplicate keys, so we don't need to first remove a key/value
1457 * pair before inserting the new key/value.
1459 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1460 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1462 break;
1463 default:
1464 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1465 wassertrv(False, NULL);
1466 break;
1469 return ret;
1472 WMPropList *WMCreatePropListFromDescription(const char *desc)
1474 WMPropList *plist = NULL;
1475 PLData *pldata;
1477 pldata = (PLData *) wmalloc(sizeof(PLData));
1478 pldata->ptr = desc;
1479 pldata->lineNumber = 1;
1481 plist = getPropList(pldata);
1483 if (getNonSpaceChar(pldata) != 0 && plist) {
1484 COMPLAIN(pldata, _("extra data after end of property list"));
1486 * We can't just ignore garbage after the end of the description
1487 * (especially if the description was read from a file), because
1488 * the "garbage" can be the real data and the real garbage is in
1489 * fact in the beginning of the file (which is now inside plist)
1491 WMReleasePropList(plist);
1492 plist = NULL;
1495 wfree(pldata);
1497 return plist;
1500 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1502 return (indented ? indentedDescription(plist, 0) : description(plist));
1505 WMPropList *WMReadPropListFromFile(const char *file)
1507 WMPropList *plist = NULL;
1508 PLData *pldata;
1509 char *read_buf;
1510 FILE *f;
1511 struct stat stbuf;
1512 size_t length;
1514 f = fopen(file, "rb");
1515 if (!f) {
1516 /* let the user print the error message if he really needs to */
1517 /*werror(_("could not open domain file '%s' for reading"), file); */
1518 return NULL;
1521 if (stat(file, &stbuf) == 0) {
1522 length = (size_t) stbuf.st_size;
1523 } else {
1524 werror(_("could not get size for file '%s'"), file);
1525 fclose(f);
1526 return NULL;
1529 read_buf = wmalloc(length + 1);
1530 if (fread(read_buf, length, 1, f) != 1) {
1531 if (ferror(f)) {
1532 werror(_("error reading from file '%s'"), file);
1534 fclose(f);
1535 wfree(read_buf);
1536 return NULL;
1538 read_buf[length] = '\0';
1539 fclose(f);
1541 pldata = (PLData *) wmalloc(sizeof(PLData));
1542 pldata->ptr = read_buf;
1543 pldata->filename = file;
1544 pldata->lineNumber = 1;
1546 plist = getPropList(pldata);
1548 if (getNonSpaceChar(pldata) != 0 && plist) {
1549 COMPLAIN(pldata, _("extra data after end of property list"));
1551 * We can't just ignore garbage after the end of the description
1552 * (especially if the description was read from a file), because
1553 * the "garbage" can be the real data and the real garbage is in
1554 * fact in the beginning of the file (which is now inside plist)
1556 WMReleasePropList(plist);
1557 plist = NULL;
1560 wfree(read_buf);
1561 wfree(pldata);
1563 return plist;
1566 WMPropList *WMReadPropListFromPipe(const char *command)
1568 FILE *file;
1569 WMPropList *plist;
1570 PLData *pldata;
1571 char line[1024];
1572 char *read_buf;
1574 file = popen(command, "r");
1576 if (!file) {
1577 werror(_("%s:could not open menu file"), command);
1578 return NULL;
1581 pldata = (PLData *) wmalloc(sizeof(PLData));
1582 pldata->ptr = NULL;
1583 pldata->filename = command;
1584 pldata->lineNumber = 1;
1586 /* read from file till EOF or OOM and fill proplist buffer*/
1587 read_buf = NULL;
1588 while (fgets(line, sizeof(line), file) != NULL) {
1589 if (read_buf == NULL) {
1590 read_buf = wmalloc(strlen(line)+1);
1591 read_buf[0] = '\0';
1592 } else {
1593 read_buf = wrealloc(read_buf,
1594 strlen(line) + strlen(read_buf) + 1);
1597 read_buf = strncat(read_buf, line, strlen(line));
1599 pldata->ptr = read_buf;
1601 pclose(file);
1603 plist = getPropList(pldata);
1605 if (getNonSpaceChar(pldata) != 0 && plist) {
1606 COMPLAIN(pldata, _("extra data after end of property list"));
1608 * We can't just ignore garbage after the end of the description
1609 * (especially if the description was read from a file), because
1610 * the "garbage" can be the real data and the real garbage is in
1611 * fact in the beginning of the file (which is now inside plist)
1613 WMReleasePropList(plist);
1614 plist = NULL;
1617 wfree(read_buf);
1618 wfree(pldata);
1620 return plist;
1623 /* TODO: review this function's code */
1625 Bool WMWritePropListToFile(WMPropList * plist, const char *path)
1627 char *thePath = NULL;
1628 char *desc;
1629 FILE *theFile;
1630 #ifdef HAVE_MKSTEMP
1631 int fd, mask;
1632 #endif
1634 if (!wmkdirhier(path))
1635 return False;
1637 /* Use the path name of the destination file as a prefix for the
1638 * mkstemp() call so that we can be sure that both files are on
1639 * the same filesystem and the subsequent rename() will work. */
1640 thePath = wstrconcat(path, ".XXXXXX");
1642 #ifdef HAVE_MKSTEMP
1643 if ((fd = mkstemp(thePath)) < 0) {
1644 werror(_("mkstemp (%s) failed"), thePath);
1645 goto failure;
1647 mask = umask(0);
1648 umask(mask);
1649 fchmod(fd, 0644 & ~mask);
1650 if ((theFile = fdopen(fd, "wb")) == NULL) {
1651 close(fd);
1653 #else
1654 if (mktemp(thePath) == NULL) {
1655 werror(_("mktemp (%s) failed"), thePath);
1656 goto failure;
1658 theFile = fopen(thePath, "wb");
1659 #endif
1661 if (theFile == NULL) {
1662 werror(_("open (%s) failed"), thePath);
1663 goto failure;
1666 desc = indentedDescription(plist, 0);
1668 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1669 werror(_("writing to file: %s failed"), thePath);
1670 wfree(desc);
1671 goto failure;
1674 wfree(desc);
1676 (void)fsync(fileno(theFile));
1677 if (fclose(theFile) != 0) {
1678 werror(_("fclose (%s) failed"), thePath);
1679 goto failure;
1682 /* If we used a temporary file, we still need to rename() it be the
1683 * real file. Also, we need to try to retain the file attributes of
1684 * the original file we are overwriting (if we are) */
1685 if (rename(thePath, path) != 0) {
1686 werror(_("rename ('%s' to '%s') failed"), thePath, path);
1687 goto failure;
1690 wfree(thePath);
1691 return True;
1693 failure:
1694 unlink(thePath);
1695 wfree(thePath);
1696 return False;
1700 * create a directory hierarchy
1702 * if the last octet of `path' is `/', the full path is
1703 * assumed to be a directory; otherwise path is assumed to be a
1704 * file, and the last component is stripped off. the rest is the
1705 * the hierarchy to be created.
1707 * refuses to create anything outside $GNUSTEP_USER_ROOT
1709 * returns 1 on success, 0 on failure
1711 int wmkdirhier(const char *path)
1713 const char *t;
1714 char *thePath = NULL, buf[1024];
1715 size_t p, plen;
1716 struct stat st;
1718 /* Only create directories under $GNUSTEP_USER_ROOT */
1719 if ((t = wusergnusteppath()) == NULL)
1720 return 0;
1721 if (strncmp(path, t, strlen(t)) != 0)
1722 return 0;
1724 thePath = wstrdup(path);
1725 /* Strip the trailing component if it is a file */
1726 p = strlen(thePath);
1727 while (p && thePath[p] != '/')
1728 thePath[p--] = '\0';
1730 thePath[p] = '\0';
1732 /* Shortcut if it already exists */
1733 if (stat(thePath, &st) == 0) {
1734 wfree(thePath);
1735 if (S_ISDIR(st.st_mode)) {
1736 /* Is a directory alright */
1737 return 1;
1738 } else {
1739 /* Exists, but not a directory, the caller
1740 * might just as well abort now */
1741 return 0;
1745 memset(buf, 0, sizeof(buf));
1746 strncpy(buf, t, sizeof(buf) - 1);
1747 p = strlen(buf);
1748 plen = strlen(thePath);
1750 do {
1751 while (p++ < plen && thePath[p] != '/')
1754 strncpy(buf, thePath, p);
1755 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1756 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1757 werror(_("Could not create component %s"), buf);
1758 wfree(thePath);
1759 return 0;
1761 } while (p < plen);
1763 wfree(thePath);
1764 return 1;
1767 /* ARGSUSED2 */
1768 static int wrmdirhier_fn(const char *path, const struct stat *st,
1769 int type, struct FTW *ftw)
1771 switch(type) {
1772 case FTW_D:
1773 break;
1774 case FTW_DP:
1775 return rmdir(path);
1776 break;
1777 case FTW_F:
1778 case FTW_SL:
1779 case FTW_SLN:
1780 return unlink(path);
1781 break;
1782 case FTW_DNR:
1783 case FTW_NS:
1784 default:
1785 return EPERM;
1788 /* NOTREACHED */
1789 return 0;
1793 * remove a directory hierarchy
1795 * refuses to remove anything outside $GNUSTEP_USER_ROOT
1797 * returns 1 on success, 0 on failure
1799 * TODO: revisit what's error and what's not
1801 * with inspirations from OpenBSD's bin/rm/rm.c
1803 int wrmdirhier(const char *path)
1805 struct stat st;
1806 int error;
1807 const char *t;
1809 /* Only remove directories under $GNUSTEP_USER_ROOT */
1810 if ((t = wusergnusteppath()) == NULL)
1811 return EPERM;
1812 if (strncmp(path, t, strlen(t)) != 0)
1813 return EPERM;
1815 /* Shortcut if it doesn't exist to begin with */
1816 if (stat(path, &st) == -1)
1817 return ENOENT;
1819 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1821 return error;