WPrefs: link against math library because it is used in a few places
[wmaker-crm.git] / WINGs / proplist.c
blob28a3ae26e4e1d06523cdece20556d6da91be24b7
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(const void *param);
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 Bool(*isEqualFunc) (const void *, const void *);
60 static const WMHashTableCallbacks WMPropListHashCallbacks = {
61 hashPropList,
62 (isEqualFunc) WMIsPropListEqualTo,
63 NULL,
64 NULL
67 static Bool caseSensitive = True;
69 #define BUFFERSIZE 8192
70 #define BUFFERSIZE_INCREMENT 1024
72 #if 0
73 # define DPUT(s) puts(s)
74 #else
75 # define DPUT(s)
76 #endif
78 #define COMPLAIN(pld, msg) wwarning(_("syntax error in %s %s, line %i: %s"),\
79 (pld)->filename ? "file" : "PropList",\
80 (pld)->filename ? (pld)->filename : "description",\
81 (pld)->lineNumber, msg)
83 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
84 || (c)=='+')
86 #define CHECK_BUFFER_SIZE(buf, ptr) \
87 if ((ptr) >= (buf).size-1) {\
88 (buf).size += BUFFERSIZE_INCREMENT;\
89 (buf).str = wrealloc((buf).str, (buf).size);\
92 #define inrange(ch, min, max) ((ch)>=(min) && (ch)<=(max))
93 #define noquote(ch) (inrange(ch, 'a', 'z') || inrange(ch, 'A', 'Z') || inrange(ch, '0', '9') || ((ch)=='_') || ((ch)=='.') || ((ch)=='$'))
94 #define charesc(ch) (inrange(ch, 0x07, 0x0c) || ((ch)=='"') || ((ch)=='\\'))
95 #define numesc(ch) (((ch)<=0x06) || inrange(ch, 0x0d, 0x1f) || ((ch)>0x7e))
96 #define ishexdigit(ch) (inrange(ch, 'a', 'f') || inrange(ch, 'A', 'F') || inrange(ch, '0', '9'))
97 #define char2num(ch) (inrange(ch,'0','9') ? ((ch)-'0') : (inrange(ch,'a','f') ? ((ch)-0x57) : ((ch)-0x37)))
98 #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57))
100 #define MaxHashLength 64
102 static unsigned hashPropList(const void *param)
104 WMPropList *plist= (WMPropList *) param;
105 unsigned ret = 0;
106 unsigned ctr = 0;
107 const char *key;
108 int i, len;
110 switch (plist->type) {
111 case WPLString:
112 key = plist->d.string;
113 len = WMIN(strlen(key), MaxHashLength);
114 for (i = 0; i < len; i++) {
115 ret ^= tolower(key[i]) << ctr;
116 ctr = (ctr + 1) % sizeof(char *);
118 /*while (*key) {
119 ret ^= tolower(*key++) << ctr;
120 ctr = (ctr + 1) % sizeof (char *);
121 } */
122 break;
124 case WPLData:
125 key = WMDataBytes(plist->d.data);
126 len = WMIN(WMGetDataLength(plist->d.data), MaxHashLength);
127 for (i = 0; i < len; i++) {
128 ret ^= key[i] << ctr;
129 ctr = (ctr + 1) % sizeof(char *);
131 break;
133 default:
134 wwarning(_("Only string or data is supported for a proplist dictionary key"));
135 wassertrv(False, 0);
136 break;
139 return ret;
142 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
144 WMPropList *key, *value;
145 WMHashEnumerator e;
146 int i;
148 plist->retainCount += count;
150 switch (plist->type) {
151 case WPLString:
152 case WPLData:
153 break;
154 case WPLArray:
155 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
156 retainPropListByCount(WMGetFromArray(plist->d.array, i), count);
158 break;
159 case WPLDictionary:
160 e = WMEnumerateHashTable(plist->d.dict);
161 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
162 retainPropListByCount(key, count);
163 retainPropListByCount(value, count);
165 break;
166 default:
167 wwarning(_("Used proplist functions on non-WMPropLists objects"));
168 wassertrv(False, NULL);
169 break;
172 return plist;
175 static void releasePropListByCount(WMPropList * plist, int count)
177 WMPropList *key, *value;
178 WMHashEnumerator e;
179 int i;
181 plist->retainCount -= count;
183 switch (plist->type) {
184 case WPLString:
185 if (plist->retainCount < 1) {
186 wfree(plist->d.string);
187 wfree(plist);
189 break;
190 case WPLData:
191 if (plist->retainCount < 1) {
192 WMReleaseData(plist->d.data);
193 wfree(plist);
195 break;
196 case WPLArray:
197 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
198 releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
200 if (plist->retainCount < 1) {
201 WMFreeArray(plist->d.array);
202 wfree(plist);
204 break;
205 case WPLDictionary:
206 e = WMEnumerateHashTable(plist->d.dict);
207 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
208 releasePropListByCount(key, count);
209 releasePropListByCount(value, count);
211 if (plist->retainCount < 1) {
212 WMFreeHashTable(plist->d.dict);
213 wfree(plist);
215 break;
216 default:
217 wwarning(_("Used proplist functions on non-WMPropLists objects"));
218 wassertr(False);
219 break;
223 static char *dataDescription(WMPropList * plist)
225 const unsigned char *data;
226 char *retVal;
227 int i, j, length;
229 data = WMDataBytes(plist->d.data);
230 length = WMGetDataLength(plist->d.data);
232 retVal = (char *)wmalloc(2 * length + length / 4 + 3);
234 retVal[0] = '<';
235 for (i = 0, j = 1; i < length; i++) {
236 retVal[j++] = num2char((data[i] >> 4) & 0x0f);
237 retVal[j++] = num2char(data[i] & 0x0f);
238 if ((i & 0x03) == 3 && i != length - 1) {
239 /* if we've just finished a 32-bit int, add a space */
240 retVal[j++] = ' ';
243 retVal[j++] = '>';
244 retVal[j] = '\0';
246 return retVal;
249 static char *stringDescription(WMPropList * plist)
251 const char *str;
252 char *retVal, *sPtr, *dPtr;
253 int len, quote;
254 unsigned char ch;
256 str = plist->d.string;
258 if (strlen(str) == 0) {
259 return wstrdup("\"\"");
262 /* FIXME: make this work with unichars. */
264 quote = 0;
265 sPtr = (char *)str;
266 len = 0;
267 while ((ch = *sPtr)) {
268 if (!noquote(ch)) {
269 quote = 1;
270 if (charesc(ch))
271 len++;
272 else if (numesc(ch))
273 len += 3;
275 sPtr++;
276 len++;
279 if (quote)
280 len += 2;
282 retVal = (char *)wmalloc(len + 1);
284 sPtr = (char *)str;
285 dPtr = retVal;
287 if (quote)
288 *dPtr++ = '"';
290 while ((ch = *sPtr)) {
291 if (charesc(ch)) {
292 *(dPtr++) = '\\';
293 switch (ch) {
294 case '\a':
295 *dPtr = 'a';
296 break;
297 case '\b':
298 *dPtr = 'b';
299 break;
300 case '\t':
301 *dPtr = 't';
302 break;
303 case '\n':
304 *dPtr = 'n';
305 break;
306 case '\v':
307 *dPtr = 'v';
308 break;
309 case '\f':
310 *dPtr = 'f';
311 break;
312 default:
313 *dPtr = ch; /* " or \ */
315 } else if (numesc(ch)) {
316 *(dPtr++) = '\\';
317 *(dPtr++) = '0' + ((ch >> 6) & 07);
318 *(dPtr++) = '0' + ((ch >> 3) & 07);
319 *dPtr = '0' + (ch & 07);
320 } else {
321 *dPtr = ch;
323 sPtr++;
324 dPtr++;
327 if (quote)
328 *dPtr++ = '"';
330 *dPtr = '\0';
332 return retVal;
335 static char *description(WMPropList * plist)
337 WMPropList *key, *val;
338 char *retstr = NULL;
339 char *str, *tmp, *skey, *sval;
340 WMHashEnumerator e;
341 int i;
343 switch (plist->type) {
344 case WPLString:
345 retstr = stringDescription(plist);
346 break;
347 case WPLData:
348 retstr = dataDescription(plist);
349 break;
350 case WPLArray:
351 retstr = wstrdup("(");
352 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
353 str = description(WMGetFromArray(plist->d.array, i));
354 if (i == 0) {
355 retstr = wstrappend(retstr, str);
356 } else {
357 tmp = (char *)wmalloc(strlen(retstr) + strlen(str) + 3);
358 sprintf(tmp, "%s, %s", retstr, str);
359 wfree(retstr);
360 retstr = tmp;
362 wfree(str);
364 retstr = wstrappend(retstr, ")");
365 break;
366 case WPLDictionary:
367 retstr = wstrdup("{");
368 e = WMEnumerateHashTable(plist->d.dict);
369 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
370 skey = description(key);
371 sval = description(val);
372 tmp = (char *)wmalloc(strlen(retstr) + strlen(skey) + strlen(sval) + 5);
373 sprintf(tmp, "%s%s = %s;", retstr, skey, sval);
374 wfree(skey);
375 wfree(sval);
376 wfree(retstr);
377 retstr = tmp;
379 retstr = wstrappend(retstr, "}");
380 break;
381 default:
382 wwarning(_("Used proplist functions on non-WMPropLists objects"));
383 wassertrv(False, NULL);
384 break;
387 return retstr;
390 static char *indentedDescription(WMPropList * plist, int level)
392 WMPropList *key, *val;
393 char *retstr = NULL;
394 char *str, *tmp, *skey, *sval;
395 WMHashEnumerator e;
396 int i;
398 if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
399 retstr = description(plist);
401 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
402 return retstr;
403 } else if (retstr) {
404 wfree(retstr);
405 retstr = NULL;
409 switch (plist->type) {
410 case WPLString:
411 retstr = stringDescription(plist);
412 break;
413 case WPLData:
414 retstr = dataDescription(plist);
415 break;
416 case WPLArray:
417 retstr = wstrdup("(\n");
418 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
419 str = indentedDescription(WMGetFromArray(plist->d.array, i), level + 1);
420 if (i == 0) {
421 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 1);
422 sprintf(tmp, "%s%*s%s", retstr, 2 * (level + 1), "", str);
423 wfree(retstr);
424 retstr = tmp;
425 } else {
426 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(str) + 3);
427 sprintf(tmp, "%s,\n%*s%s", retstr, 2 * (level + 1), "", str);
428 wfree(retstr);
429 retstr = tmp;
431 wfree(str);
433 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 3);
434 sprintf(tmp, "%s\n%*s)", retstr, 2 * level, "");
435 wfree(retstr);
436 retstr = tmp;
437 break;
438 case WPLDictionary:
439 retstr = wstrdup("{\n");
440 e = WMEnumerateHashTable(plist->d.dict);
441 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&val, (void **)&key)) {
442 skey = indentedDescription(key, level + 1);
443 sval = indentedDescription(val, level + 1);
444 tmp = (char *)wmalloc(2 * (level + 1) + strlen(retstr) + strlen(skey)
445 + strlen(sval) + 6);
446 sprintf(tmp, "%s%*s%s = %s;\n", retstr, 2 * (level + 1), "", skey, sval);
447 wfree(skey);
448 wfree(sval);
449 wfree(retstr);
450 retstr = tmp;
452 tmp = (char *)wmalloc(strlen(retstr) + 2 * level + 2);
453 sprintf(tmp, "%s%*s}", retstr, 2 * level, "");
454 wfree(retstr);
455 retstr = tmp;
456 break;
457 default:
458 wwarning(_("Used proplist functions on non-WMPropLists objects"));
459 wassertrv(False, NULL);
460 break;
463 return retstr;
466 static inline int getChar(PLData * pldata)
468 int c;
470 c = pldata->ptr[pldata->pos];
471 if (c == 0) {
472 return 0;
475 pldata->pos++;
477 if (c == '\n')
478 pldata->lineNumber++;
480 return c;
483 static inline int getNonSpaceChar(PLData * pldata)
485 int c;
487 while (1) {
488 c = pldata->ptr[pldata->pos];
489 if (c == 0) {
490 break;
492 pldata->pos++;
493 if (c == '\n') {
494 pldata->lineNumber++;
495 } else if (!isspace(c)) {
496 break;
500 return c;
503 static char *unescapestr(const char *src)
505 char *dest = wmalloc(strlen(src) + 1);
506 char *dPtr;
507 char ch;
509 for (dPtr = dest; ; dPtr++) {
510 ch = *src++;
511 if (ch == '\0')
512 break;
513 else if (ch != '\\')
514 *dPtr = ch;
515 else {
516 ch = *(src++);
517 if (ch == '\0') {
518 *dPtr = '\\';
519 break;
520 } else if ((ch >= '0') && (ch <= '7')) {
521 char wch;
523 /* Convert octal number to character */
524 wch = (ch & 07);
525 ch = *src;
526 if ((ch >= '0') && (ch <= '7')) {
527 src++;
528 wch = (wch << 3) | (ch & 07);
529 ch = *src;
530 if ((ch >= '0') && (ch <= '7')) {
531 src++;
532 wch = (wch << 3) | (ch & 07);
535 *dPtr = wch;
536 } else {
537 switch (ch) {
538 case 'a':
539 *dPtr = '\a';
540 break;
541 case 'b':
542 *dPtr = '\b';
543 break;
544 case 't':
545 *dPtr = '\t';
546 break;
547 case 'r':
548 *dPtr = '\r';
549 break;
550 case 'n':
551 *dPtr = '\n';
552 break;
553 case 'v':
554 *dPtr = '\v';
555 break;
556 case 'f':
557 *dPtr = '\f';
558 break;
559 default:
560 *dPtr = ch;
566 *dPtr = 0;
568 return dest;
571 static WMPropList *getPLString(PLData * pldata)
573 WMPropList *plist;
574 StringBuffer sBuf;
575 int ptr = 0;
576 int c;
578 sBuf.str = wmalloc(BUFFERSIZE);
579 sBuf.size = BUFFERSIZE;
581 while (1) {
582 c = getChar(pldata);
583 if (ISSTRINGABLE(c)) {
584 CHECK_BUFFER_SIZE(sBuf, ptr);
585 sBuf.str[ptr++] = c;
586 } else {
587 if (c != 0) {
588 pldata->pos--;
590 break;
594 sBuf.str[ptr] = 0;
596 if (ptr == 0) {
597 plist = NULL;
598 } else {
599 char *tmp = unescapestr(sBuf.str);
600 plist = WMCreatePLString(tmp);
601 wfree(tmp);
604 wfree(sBuf.str);
606 return plist;
609 static WMPropList *getPLQString(PLData * pldata)
611 WMPropList *plist;
612 int ptr = 0, escaping = 0, ok = 1;
613 int c;
614 StringBuffer sBuf;
616 sBuf.str = wmalloc(BUFFERSIZE);
617 sBuf.size = BUFFERSIZE;
619 while (1) {
620 c = getChar(pldata);
621 if (!escaping) {
622 if (c == '\\') {
623 escaping = 1;
624 continue;
625 } else if (c == '"') {
626 break;
628 } else {
629 CHECK_BUFFER_SIZE(sBuf, ptr);
630 sBuf.str[ptr++] = '\\';
631 escaping = 0;
634 if (c == 0) {
635 COMPLAIN(pldata, _("unterminated PropList string"));
636 ok = 0;
637 break;
638 } else {
639 CHECK_BUFFER_SIZE(sBuf, ptr);
640 sBuf.str[ptr++] = c;
644 sBuf.str[ptr] = 0;
646 if (!ok) {
647 plist = NULL;
648 } else {
649 char *tmp = unescapestr(sBuf.str);
650 plist = WMCreatePLString(tmp);
651 wfree(tmp);
654 wfree(sBuf.str);
656 return plist;
659 static WMPropList *getPLData(PLData * pldata)
661 int ok = 1;
662 int len = 0;
663 int c1, c2;
664 unsigned char buf[BUFFERSIZE], byte;
665 WMPropList *plist;
666 WMData *data;
668 data = WMCreateDataWithCapacity(0);
670 while (1) {
671 c1 = getNonSpaceChar(pldata);
672 if (c1 == 0) {
673 COMPLAIN(pldata, _("unterminated PropList data"));
674 ok = 0;
675 break;
676 } else if (c1 == '>') {
677 break;
678 } else if (ishexdigit(c1)) {
679 c2 = getNonSpaceChar(pldata);
680 if (c2 == 0 || c2 == '>') {
681 COMPLAIN(pldata, _("unterminated PropList data (missing hexdigit)"));
682 ok = 0;
683 break;
684 } else if (ishexdigit(c2)) {
685 byte = char2num(c1) << 4;
686 byte |= char2num(c2);
687 buf[len++] = byte;
688 if (len == sizeof(buf)) {
689 WMAppendDataBytes(data, buf, len);
690 len = 0;
692 } else {
693 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
694 ok = 0;
695 break;
697 } else {
698 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
699 ok = 0;
700 break;
704 if (!ok) {
705 WMReleaseData(data);
706 return NULL;
709 if (len > 0)
710 WMAppendDataBytes(data, buf, len);
712 plist = WMCreatePLData(data);
713 WMReleaseData(data);
715 return plist;
718 static WMPropList *getPLArray(PLData * pldata)
720 Bool first = True;
721 int ok = 1;
722 int c;
723 WMPropList *array, *obj;
725 array = WMCreatePLArray(NULL);
727 while (1) {
728 c = getNonSpaceChar(pldata);
729 if (c == 0) {
730 COMPLAIN(pldata, _("unterminated PropList array"));
731 ok = 0;
732 break;
733 } else if (c == ')') {
734 break;
735 } else if (c == ',') {
736 /* continue normally */
737 } else if (!first) {
738 COMPLAIN(pldata, _("missing or unterminated PropList array"));
739 ok = 0;
740 break;
741 } else {
742 pldata->pos--;
744 first = False;
746 obj = getPropList(pldata);
747 if (!obj) {
748 COMPLAIN(pldata, _("could not get PropList array element"));
749 ok = 0;
750 break;
752 WMAddToPLArray(array, obj);
753 WMReleasePropList(obj);
756 if (!ok) {
757 WMReleasePropList(array);
758 array = NULL;
761 return array;
764 static WMPropList *getPLDictionary(PLData * pldata)
766 int ok = 1;
767 int c;
768 WMPropList *dict, *key, *value;
770 dict = WMCreatePLDictionary(NULL, NULL);
772 while (1) {
773 c = getNonSpaceChar(pldata);
774 if (c == 0) {
775 COMPLAIN(pldata, _("unterminated PropList dictionary"));
776 ok = 0;
777 break;
778 } else if (c == '}') {
779 break;
782 DPUT("getting PropList dictionary key");
783 if (c == '<') {
784 key = getPLData(pldata);
785 } else if (c == '"') {
786 key = getPLQString(pldata);
787 } else if (ISSTRINGABLE(c)) {
788 pldata->pos--;
789 key = getPLString(pldata);
790 } else {
791 if (c == '=') {
792 COMPLAIN(pldata, _("missing PropList dictionary key"));
793 } else {
794 COMPLAIN(pldata, _("missing PropList dictionary entry key "
795 "or unterminated dictionary"));
797 ok = 0;
798 break;
801 if (!key) {
802 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
803 ok = 0;
804 break;
807 c = getNonSpaceChar(pldata);
808 if (c != '=') {
809 WMReleasePropList(key);
810 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
811 ok = 0;
812 break;
815 DPUT("getting PropList dictionary entry value for key");
816 value = getPropList(pldata);
817 if (!value) {
818 COMPLAIN(pldata, _("error parsing PropList dictionary entry value"));
819 WMReleasePropList(key);
820 ok = 0;
821 break;
824 c = getNonSpaceChar(pldata);
825 if (c != ';') {
826 COMPLAIN(pldata, _("missing ; in PropList dictionary entry"));
827 WMReleasePropList(key);
828 WMReleasePropList(value);
829 ok = 0;
830 break;
833 WMPutInPLDictionary(dict, key, value);
834 WMReleasePropList(key);
835 WMReleasePropList(value);
838 if (!ok) {
839 WMReleasePropList(dict);
840 dict = NULL;
843 return dict;
846 static WMPropList *getPropList(PLData * pldata)
848 WMPropList *plist;
849 int c;
851 c = getNonSpaceChar(pldata);
853 switch (c) {
854 case 0:
855 DPUT("End of PropList");
856 plist = NULL;
857 break;
859 case '{':
860 DPUT("Getting PropList dictionary");
861 plist = getPLDictionary(pldata);
862 break;
864 case '(':
865 DPUT("Getting PropList array");
866 plist = getPLArray(pldata);
867 break;
869 case '<':
870 DPUT("Getting PropList data");
871 plist = getPLData(pldata);
872 break;
874 case '"':
875 DPUT("Getting PropList quoted string");
876 plist = getPLQString(pldata);
877 break;
879 default:
880 if (ISSTRINGABLE(c)) {
881 DPUT("Getting PropList string");
882 pldata->pos--;
883 plist = getPLString(pldata);
884 } else {
885 COMPLAIN(pldata, _("was expecting a string, data, array or "
886 "dictionary. If it's a string, try enclosing " "it with \"."));
887 if (c == '#' || c == '/') {
888 wwarning(_("Comments are not allowed inside WindowMaker owned" " domain files."));
890 plist = NULL;
892 break;
895 return plist;
898 void WMPLSetCaseSensitive(Bool caseSensitiveness)
900 caseSensitive = caseSensitiveness;
903 WMPropList *WMCreatePLString(const char *str)
905 WMPropList *plist;
907 wassertrv(str != NULL, NULL);
909 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
910 plist->type = WPLString;
911 plist->d.string = wstrdup(str);
912 plist->retainCount = 1;
914 return plist;
917 WMPropList *WMCreatePLData(WMData * data)
919 WMPropList *plist;
921 wassertrv(data != NULL, NULL);
923 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
924 plist->type = WPLData;
925 plist->d.data = WMRetainData(data);
926 plist->retainCount = 1;
928 return plist;
931 WMPropList *WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int length)
933 WMPropList *plist;
935 wassertrv(bytes != NULL, NULL);
937 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
938 plist->type = WPLData;
939 plist->d.data = WMCreateDataWithBytes(bytes, length);
940 plist->retainCount = 1;
942 return plist;
945 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
947 WMPropList *plist;
949 wassertrv(bytes != NULL, NULL);
951 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
952 plist->type = WPLData;
953 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
954 plist->retainCount = 1;
956 return plist;
959 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
961 WMPropList *plist, *nelem;
962 va_list ap;
964 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
965 plist->type = WPLArray;
966 plist->d.array = WMCreateArray(4);
967 plist->retainCount = 1;
969 if (!elem)
970 return plist;
972 WMAddToArray(plist->d.array, WMRetainPropList(elem));
974 va_start(ap, elem);
976 while (1) {
977 nelem = va_arg(ap, WMPropList *);
978 if (!nelem) {
979 va_end(ap);
980 return plist;
982 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
986 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
988 WMPropList *plist, *nkey, *nvalue, *k, *v;
989 va_list ap;
991 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
992 plist->type = WPLDictionary;
993 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
994 plist->retainCount = 1;
996 if (!key || !value)
997 return plist;
999 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
1001 va_start(ap, value);
1003 while (1) {
1004 nkey = va_arg(ap, WMPropList *);
1005 if (!nkey) {
1006 va_end(ap);
1007 return plist;
1009 nvalue = va_arg(ap, WMPropList *);
1010 if (!nvalue) {
1011 va_end(ap);
1012 return plist;
1014 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
1015 WMHashRemove(plist->d.dict, k);
1016 WMReleasePropList(k);
1017 WMReleasePropList(v);
1019 WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
1023 WMPropList *WMRetainPropList(WMPropList * plist)
1025 WMPropList *key, *value;
1026 WMHashEnumerator e;
1027 int i;
1029 plist->retainCount++;
1031 switch (plist->type) {
1032 case WPLString:
1033 case WPLData:
1034 break;
1035 case WPLArray:
1036 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1037 WMRetainPropList(WMGetFromArray(plist->d.array, i));
1039 break;
1040 case WPLDictionary:
1041 e = WMEnumerateHashTable(plist->d.dict);
1042 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1043 WMRetainPropList(key);
1044 WMRetainPropList(value);
1046 break;
1047 default:
1048 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1049 wassertrv(False, NULL);
1050 break;
1053 return plist;
1056 void WMReleasePropList(WMPropList * plist)
1058 WMPropList *key, *value;
1059 WMHashEnumerator e;
1060 int i;
1062 plist->retainCount--;
1064 switch (plist->type) {
1065 case WPLString:
1066 if (plist->retainCount < 1) {
1067 wfree(plist->d.string);
1068 wfree(plist);
1070 break;
1071 case WPLData:
1072 if (plist->retainCount < 1) {
1073 WMReleaseData(plist->d.data);
1074 wfree(plist);
1076 break;
1077 case WPLArray:
1078 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1079 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1081 if (plist->retainCount < 1) {
1082 WMFreeArray(plist->d.array);
1083 wfree(plist);
1085 break;
1086 case WPLDictionary:
1087 e = WMEnumerateHashTable(plist->d.dict);
1088 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1089 WMReleasePropList(key);
1090 WMReleasePropList(value);
1092 if (plist->retainCount < 1) {
1093 WMFreeHashTable(plist->d.dict);
1094 wfree(plist);
1096 break;
1097 default:
1098 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1099 wassertr(False);
1100 break;
1104 void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
1106 wassertr(plist->type == WPLArray);
1108 retainPropListByCount(item, plist->retainCount);
1109 WMInsertInArray(plist->d.array, index, item);
1112 void WMAddToPLArray(WMPropList * plist, WMPropList * item)
1114 wassertr(plist->type == WPLArray);
1116 retainPropListByCount(item, plist->retainCount);
1117 WMAddToArray(plist->d.array, item);
1120 void WMDeleteFromPLArray(WMPropList * plist, int index)
1122 WMPropList *item;
1124 wassertr(plist->type == WPLArray);
1126 item = WMGetFromArray(plist->d.array, index);
1127 if (item != NULL) {
1128 WMDeleteFromArray(plist->d.array, index);
1129 releasePropListByCount(item, plist->retainCount);
1133 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1135 WMPropList *iPtr;
1136 int i;
1138 wassertr(plist->type == WPLArray);
1140 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1141 iPtr = WMGetFromArray(plist->d.array, i);
1142 if (WMIsPropListEqualTo(item, iPtr)) {
1143 WMDeleteFromArray(plist->d.array, i);
1144 releasePropListByCount(iPtr, plist->retainCount);
1145 break;
1150 void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
1152 wassertr(plist->type == WPLDictionary);
1154 /*WMRetainPropList(key); */
1155 WMRemoveFromPLDictionary(plist, key);
1156 retainPropListByCount(key, plist->retainCount);
1157 retainPropListByCount(value, plist->retainCount);
1158 WMHashInsert(plist->d.dict, key, value);
1159 /*WMReleasePropList(key); */
1162 void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
1164 WMPropList *k, *v;
1166 wassertr(plist->type == WPLDictionary);
1168 if (WMHashGetItemAndKey(plist->d.dict, key, (void **)&v, (void **)&k)) {
1169 WMHashRemove(plist->d.dict, k);
1170 releasePropListByCount(k, plist->retainCount);
1171 releasePropListByCount(v, plist->retainCount);
1175 WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1177 WMPropList *key, *value, *dvalue;
1178 WMHashEnumerator e;
1180 wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
1182 if (source == dest)
1183 return dest;
1185 e = WMEnumerateHashTable(source->d.dict);
1186 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1187 if (recursive && value->type == WPLDictionary) {
1188 dvalue = WMHashGet(dest->d.dict, key);
1189 if (dvalue && dvalue->type == WPLDictionary) {
1190 WMMergePLDictionaries(dvalue, value, True);
1191 } else {
1192 WMPutInPLDictionary(dest, key, value);
1194 } else {
1195 WMPutInPLDictionary(dest, key, value);
1199 return dest;
1202 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1204 WMPropList *key, *value, *dvalue;
1205 WMHashEnumerator e;
1207 wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
1209 if (source == dest) {
1210 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1211 int i;
1213 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1214 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1216 return dest;
1219 e = WMEnumerateHashTable(source->d.dict);
1220 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1221 dvalue = WMHashGet(dest->d.dict, key);
1222 if (!dvalue)
1223 continue;
1224 if (WMIsPropListEqualTo(value, dvalue)) {
1225 WMRemoveFromPLDictionary(dest, key);
1226 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1227 WMSubtractPLDictionaries(dvalue, value, True);
1231 return dest;
1234 int WMGetPropListItemCount(WMPropList * plist)
1236 switch (plist->type) {
1237 case WPLString:
1238 case WPLData:
1239 return 0; /* should this be 1 instead? */
1240 case WPLArray:
1241 return WMGetArrayItemCount(plist->d.array);
1242 case WPLDictionary:
1243 return (int)WMCountHashTable(plist->d.dict);
1244 default:
1245 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1246 wassertrv(False, 0);
1247 break;
1250 return 0;
1253 Bool WMIsPLString(WMPropList * plist)
1255 return (plist->type == WPLString);
1258 Bool WMIsPLData(WMPropList * plist)
1260 return (plist->type == WPLData);
1263 Bool WMIsPLArray(WMPropList * plist)
1265 return (plist->type == WPLArray);
1268 Bool WMIsPLDictionary(WMPropList * plist)
1270 return (plist->type == WPLDictionary);
1273 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1275 WMPropList *key1, *item1, *item2;
1276 WMHashEnumerator enumerator;
1277 int n, i;
1279 if (plist->type != other->type)
1280 return False;
1282 switch (plist->type) {
1283 case WPLString:
1284 if (caseSensitive) {
1285 return (strcmp(plist->d.string, other->d.string) == 0);
1286 } else {
1287 return (strcasecmp(plist->d.string, other->d.string) == 0);
1289 case WPLData:
1290 return WMIsDataEqualToData(plist->d.data, other->d.data);
1291 case WPLArray:
1292 n = WMGetArrayItemCount(plist->d.array);
1293 if (n != WMGetArrayItemCount(other->d.array))
1294 return False;
1295 for (i = 0; i < n; i++) {
1296 item1 = WMGetFromArray(plist->d.array, i);
1297 item2 = WMGetFromArray(other->d.array, i);
1298 if (!WMIsPropListEqualTo(item1, item2))
1299 return False;
1301 return True;
1302 case WPLDictionary:
1303 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1304 return False;
1305 enumerator = WMEnumerateHashTable(plist->d.dict);
1306 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1307 item2 = WMHashGet(other->d.dict, key1);
1308 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1309 return False;
1311 return True;
1312 default:
1313 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1314 wassertrv(False, False);
1315 break;
1318 return False;
1321 char *WMGetFromPLString(WMPropList * plist)
1323 wassertrv(plist->type == WPLString, NULL);
1325 return plist->d.string;
1328 WMData *WMGetFromPLData(WMPropList * plist)
1330 wassertrv(plist->type == WPLData, NULL);
1332 return plist->d.data;
1335 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1337 wassertrv(plist->type == WPLData, NULL);
1339 return WMDataBytes(plist->d.data);
1342 int WMGetPLDataLength(WMPropList * plist)
1344 wassertrv(plist->type == WPLData, 0);
1346 return WMGetDataLength(plist->d.data);
1349 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1351 wassertrv(plist->type == WPLArray, NULL);
1353 return WMGetFromArray(plist->d.array, index);
1356 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1358 wassertrv(plist->type == WPLDictionary, NULL);
1360 return WMHashGet(plist->d.dict, key);
1363 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1365 WMPropList *array, *key;
1366 WMHashEnumerator enumerator;
1368 wassertrv(plist->type == WPLDictionary, NULL);
1370 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1371 array->type = WPLArray;
1372 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1373 array->retainCount = 1;
1375 enumerator = WMEnumerateHashTable(plist->d.dict);
1376 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1377 WMAddToArray(array->d.array, WMRetainPropList(key));
1380 return array;
1383 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1385 WMPropList *ret = NULL;
1386 WMPropList *key, *item;
1387 WMHashEnumerator e;
1388 WMData *data;
1389 int i;
1391 switch (plist->type) {
1392 case WPLString:
1393 ret = WMCreatePLString(plist->d.string);
1394 break;
1395 case WPLData:
1396 data = WMCreateDataWithData(plist->d.data);
1397 ret = WMCreatePLData(data);
1398 WMReleaseData(data);
1399 break;
1400 case WPLArray:
1401 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1402 ret->type = WPLArray;
1403 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1404 ret->retainCount = 1;
1406 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1407 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1409 break;
1410 case WPLDictionary:
1411 ret = WMCreatePLDictionary(NULL, NULL);
1412 e = WMEnumerateHashTable(plist->d.dict);
1413 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1414 WMPutInPLDictionary(ret, key, item);
1416 break;
1417 default:
1418 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1419 wassertrv(False, NULL);
1420 break;
1423 return ret;
1426 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1428 WMPropList *ret = NULL;
1429 WMPropList *key, *item;
1430 WMHashEnumerator e;
1431 WMData *data;
1432 int i;
1434 switch (plist->type) {
1435 case WPLString:
1436 ret = WMCreatePLString(plist->d.string);
1437 break;
1438 case WPLData:
1439 data = WMCreateDataWithData(plist->d.data);
1440 ret = WMCreatePLData(data);
1441 WMReleaseData(data);
1442 break;
1443 case WPLArray:
1444 ret = WMCreatePLArray(NULL);
1445 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1446 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1447 WMAddToArray(ret->d.array, item);
1449 break;
1450 case WPLDictionary:
1451 ret = WMCreatePLDictionary(NULL, NULL);
1452 e = WMEnumerateHashTable(plist->d.dict);
1453 /* While we copy an existing dictionary there is no way that we can
1454 * have duplicate keys, so we don't need to first remove a key/value
1455 * pair before inserting the new key/value.
1457 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1458 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1460 break;
1461 default:
1462 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1463 wassertrv(False, NULL);
1464 break;
1467 return ret;
1470 WMPropList *WMCreatePropListFromDescription(const char *desc)
1472 WMPropList *plist = NULL;
1473 PLData *pldata;
1475 pldata = (PLData *) wmalloc(sizeof(PLData));
1476 pldata->ptr = desc;
1477 pldata->lineNumber = 1;
1479 plist = getPropList(pldata);
1481 if (getNonSpaceChar(pldata) != 0 && plist) {
1482 COMPLAIN(pldata, _("extra data after end of property list"));
1484 * We can't just ignore garbage after the end of the description
1485 * (especially if the description was read from a file), because
1486 * the "garbage" can be the real data and the real garbage is in
1487 * fact in the beginning of the file (which is now inside plist)
1489 WMReleasePropList(plist);
1490 plist = NULL;
1493 wfree(pldata);
1495 return plist;
1498 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1500 return (indented ? indentedDescription(plist, 0) : description(plist));
1503 WMPropList *WMReadPropListFromFile(const char *file)
1505 WMPropList *plist = NULL;
1506 PLData *pldata;
1507 char *read_buf;
1508 FILE *f;
1509 struct stat stbuf;
1510 size_t length;
1512 f = fopen(file, "rb");
1513 if (!f) {
1514 /* let the user print the error message if he really needs to */
1515 /*werror(_("could not open domain file '%s' for reading"), file); */
1516 return NULL;
1519 if (stat(file, &stbuf) == 0) {
1520 length = (size_t) stbuf.st_size;
1521 } else {
1522 werror(_("could not get size for file '%s'"), file);
1523 fclose(f);
1524 return NULL;
1527 read_buf = wmalloc(length + 1);
1528 if (fread(read_buf, length, 1, f) != 1) {
1529 if (ferror(f)) {
1530 werror(_("error reading from file '%s'"), file);
1532 fclose(f);
1533 wfree(read_buf);
1534 return NULL;
1536 read_buf[length] = '\0';
1537 fclose(f);
1539 pldata = (PLData *) wmalloc(sizeof(PLData));
1540 pldata->ptr = read_buf;
1541 pldata->filename = file;
1542 pldata->lineNumber = 1;
1544 plist = getPropList(pldata);
1546 if (getNonSpaceChar(pldata) != 0 && plist) {
1547 COMPLAIN(pldata, _("extra data after end of property list"));
1549 * We can't just ignore garbage after the end of the description
1550 * (especially if the description was read from a file), because
1551 * the "garbage" can be the real data and the real garbage is in
1552 * fact in the beginning of the file (which is now inside plist)
1554 WMReleasePropList(plist);
1555 plist = NULL;
1558 wfree(read_buf);
1559 wfree(pldata);
1561 return plist;
1564 WMPropList *WMReadPropListFromPipe(const char *command)
1566 FILE *file;
1567 WMPropList *plist;
1568 PLData *pldata;
1569 char *read_buf, *read_ptr;
1570 size_t remain_size, line_size;
1571 const size_t block_read_size = 4096;
1572 const size_t block_read_margin = 512;
1574 file = popen(command, "r");
1576 if (!file) {
1577 werror(_("%s:could not open menu file"), command);
1578 return NULL;
1581 /* read from file till EOF or OOM and fill proplist buffer*/
1582 remain_size = block_read_size;
1583 read_buf = wmalloc(remain_size);
1584 read_ptr = read_buf;
1585 while (fgets(read_ptr, remain_size, file) != NULL) {
1586 line_size = strlen(read_ptr);
1588 remain_size -= line_size;
1589 read_ptr += line_size;
1591 if (remain_size < block_read_margin) {
1592 size_t read_length;
1594 read_length = read_ptr - read_buf;
1595 read_buf = wrealloc(read_buf, read_length + block_read_size);
1596 read_ptr = read_buf + read_length;
1597 remain_size = block_read_size;
1601 pclose(file);
1603 pldata = (PLData *) wmalloc(sizeof(PLData));
1604 pldata->ptr = read_buf;
1605 pldata->filename = command;
1606 pldata->lineNumber = 1;
1608 plist = getPropList(pldata);
1610 if (getNonSpaceChar(pldata) != 0 && plist) {
1611 COMPLAIN(pldata, _("extra data after end of property list"));
1613 * We can't just ignore garbage after the end of the description
1614 * (especially if the description was read from a file), because
1615 * the "garbage" can be the real data and the real garbage is in
1616 * fact in the beginning of the file (which is now inside plist)
1618 WMReleasePropList(plist);
1619 plist = NULL;
1622 wfree(read_buf);
1623 wfree(pldata);
1625 return plist;
1628 /* TODO: review this function's code */
1630 Bool WMWritePropListToFile(WMPropList * plist, const char *path)
1632 char *thePath = NULL;
1633 char *desc;
1634 FILE *theFile;
1635 #ifdef HAVE_MKSTEMP
1636 int fd, mask;
1637 #endif
1639 if (!wmkdirhier(path))
1640 return False;
1642 /* Use the path name of the destination file as a prefix for the
1643 * mkstemp() call so that we can be sure that both files are on
1644 * the same filesystem and the subsequent rename() will work. */
1645 thePath = wstrconcat(path, ".XXXXXX");
1647 #ifdef HAVE_MKSTEMP
1649 * We really just want to read the current umask, but as Coverity is
1650 * pointing a possible security issue:
1651 * some versions of mkstemp do not set file rights properly on the
1652 * created file, so it is recommended so set the umask beforehand.
1653 * As we need to set an umask to read the current value, we take this
1654 * opportunity to set a temporary aggresive umask so Coverity won't
1655 * complain, even if we do not really care in the present use case.
1657 mask = umask(S_IRWXG | S_IRWXO);
1658 if ((fd = mkstemp(thePath)) < 0) {
1659 werror(_("mkstemp (%s) failed"), thePath);
1660 goto failure;
1662 umask(mask);
1663 fchmod(fd, 0666 & ~mask);
1664 if ((theFile = fdopen(fd, "wb")) == NULL) {
1665 close(fd);
1667 #else
1668 if (mktemp(thePath) == NULL) {
1669 werror(_("mktemp (%s) failed"), thePath);
1670 goto failure;
1672 theFile = fopen(thePath, "wb");
1673 #endif
1675 if (theFile == NULL) {
1676 werror(_("open (%s) failed"), thePath);
1677 goto failure;
1680 desc = indentedDescription(plist, 0);
1682 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1683 werror(_("writing to file: %s failed"), thePath);
1684 wfree(desc);
1685 goto failure;
1688 wfree(desc);
1690 (void)fsync(fileno(theFile));
1691 if (fclose(theFile) != 0) {
1692 werror(_("fclose (%s) failed"), thePath);
1693 goto failure;
1696 /* If we used a temporary file, we still need to rename() it be the
1697 * real file. Also, we need to try to retain the file attributes of
1698 * the original file we are overwriting (if we are) */
1699 if (rename(thePath, path) != 0) {
1700 werror(_("rename ('%s' to '%s') failed"), thePath, path);
1701 goto failure;
1704 wfree(thePath);
1705 return True;
1707 failure:
1708 unlink(thePath);
1709 wfree(thePath);
1710 return False;
1714 * create a directory hierarchy
1716 * if the last octet of `path' is `/', the full path is
1717 * assumed to be a directory; otherwise path is assumed to be a
1718 * file, and the last component is stripped off. the rest is the
1719 * the hierarchy to be created.
1721 * refuses to create anything outside $GNUSTEP_USER_ROOT
1723 * returns 1 on success, 0 on failure
1725 int wmkdirhier(const char *path)
1727 const char *t;
1728 char *thePath = NULL, buf[1024];
1729 size_t p, plen;
1730 struct stat st;
1732 /* Only create directories under $GNUSTEP_USER_ROOT */
1733 if ((t = wusergnusteppath()) == NULL)
1734 return 0;
1735 if (strncmp(path, t, strlen(t)) != 0)
1736 return 0;
1738 thePath = wstrdup(path);
1739 /* Strip the trailing component if it is a file */
1740 p = strlen(thePath);
1741 while (p && thePath[p] != '/')
1742 thePath[p--] = '\0';
1744 thePath[p] = '\0';
1746 /* Shortcut if it already exists */
1747 if (stat(thePath, &st) == 0) {
1748 wfree(thePath);
1749 if (S_ISDIR(st.st_mode)) {
1750 /* Is a directory alright */
1751 return 1;
1752 } else {
1753 /* Exists, but not a directory, the caller
1754 * might just as well abort now */
1755 return 0;
1759 memset(buf, 0, sizeof(buf));
1760 strncpy(buf, t, sizeof(buf) - 1);
1761 p = strlen(buf);
1762 plen = strlen(thePath);
1764 do {
1765 while (p++ < plen && thePath[p] != '/')
1768 strncpy(buf, thePath, p);
1769 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1770 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1771 werror(_("Could not create component %s"), buf);
1772 wfree(thePath);
1773 return 0;
1775 } while (p < plen);
1777 wfree(thePath);
1778 return 1;
1781 /* ARGSUSED2 */
1782 static int wrmdirhier_fn(const char *path, const struct stat *st,
1783 int type, struct FTW *ftw)
1785 /* Parameter not used, but tell the compiler that it is ok */
1786 (void) st;
1787 (void) ftw;
1789 switch(type) {
1790 case FTW_D:
1791 break;
1792 case FTW_DP:
1793 return rmdir(path);
1794 break;
1795 case FTW_F:
1796 case FTW_SL:
1797 case FTW_SLN:
1798 return unlink(path);
1799 break;
1800 case FTW_DNR:
1801 case FTW_NS:
1802 default:
1803 return EPERM;
1806 /* NOTREACHED */
1807 return 0;
1811 * remove a directory hierarchy
1813 * refuses to remove anything outside $GNUSTEP_USER_ROOT
1815 * returns 1 on success, 0 on failure
1817 * TODO: revisit what's error and what's not
1819 * with inspirations from OpenBSD's bin/rm/rm.c
1821 int wrmdirhier(const char *path)
1823 struct stat st;
1824 int error;
1825 const char *t;
1827 /* Only remove directories under $GNUSTEP_USER_ROOT */
1828 if ((t = wusergnusteppath()) == NULL)
1829 return EPERM;
1830 if (strncmp(path, t, strlen(t)) != 0)
1831 return EPERM;
1833 /* Shortcut if it doesn't exist to begin with */
1834 if (stat(path, &st) == -1)
1835 return ENOENT;
1837 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1839 return error;