menu: For consistency, use appearance.menu for English-language menus.
[wmaker-crm.git] / WINGs / proplist.c
blob5f68eace87b8327f2cfc74a164f5fb64c6442fb4
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 WMReleasePropList(keys);
1217 return dest;
1220 e = WMEnumerateHashTable(source->d.dict);
1221 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&value, (void **)&key)) {
1222 dvalue = WMHashGet(dest->d.dict, key);
1223 if (!dvalue)
1224 continue;
1225 if (WMIsPropListEqualTo(value, dvalue)) {
1226 WMRemoveFromPLDictionary(dest, key);
1227 } else if (recursive && value->type == WPLDictionary && dvalue->type == WPLDictionary) {
1228 WMSubtractPLDictionaries(dvalue, value, True);
1232 return dest;
1235 int WMGetPropListItemCount(WMPropList * plist)
1237 switch (plist->type) {
1238 case WPLString:
1239 case WPLData:
1240 return 0; /* should this be 1 instead? */
1241 case WPLArray:
1242 return WMGetArrayItemCount(plist->d.array);
1243 case WPLDictionary:
1244 return (int)WMCountHashTable(plist->d.dict);
1245 default:
1246 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1247 wassertrv(False, 0);
1248 break;
1251 return 0;
1254 Bool WMIsPLString(WMPropList * plist)
1256 return (plist->type == WPLString);
1259 Bool WMIsPLData(WMPropList * plist)
1261 return (plist->type == WPLData);
1264 Bool WMIsPLArray(WMPropList * plist)
1266 return (plist->type == WPLArray);
1269 Bool WMIsPLDictionary(WMPropList * plist)
1271 return (plist->type == WPLDictionary);
1274 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1276 WMPropList *key1, *item1, *item2;
1277 WMHashEnumerator enumerator;
1278 int n, i;
1280 if (plist->type != other->type)
1281 return False;
1283 switch (plist->type) {
1284 case WPLString:
1285 if (caseSensitive) {
1286 return (strcmp(plist->d.string, other->d.string) == 0);
1287 } else {
1288 return (strcasecmp(plist->d.string, other->d.string) == 0);
1290 case WPLData:
1291 return WMIsDataEqualToData(plist->d.data, other->d.data);
1292 case WPLArray:
1293 n = WMGetArrayItemCount(plist->d.array);
1294 if (n != WMGetArrayItemCount(other->d.array))
1295 return False;
1296 for (i = 0; i < n; i++) {
1297 item1 = WMGetFromArray(plist->d.array, i);
1298 item2 = WMGetFromArray(other->d.array, i);
1299 if (!WMIsPropListEqualTo(item1, item2))
1300 return False;
1302 return True;
1303 case WPLDictionary:
1304 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1305 return False;
1306 enumerator = WMEnumerateHashTable(plist->d.dict);
1307 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1308 item2 = WMHashGet(other->d.dict, key1);
1309 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1310 return False;
1312 return True;
1313 default:
1314 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1315 wassertrv(False, False);
1316 break;
1319 return False;
1322 char *WMGetFromPLString(WMPropList * plist)
1324 wassertrv(plist->type == WPLString, NULL);
1326 return plist->d.string;
1329 WMData *WMGetFromPLData(WMPropList * plist)
1331 wassertrv(plist->type == WPLData, NULL);
1333 return plist->d.data;
1336 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1338 wassertrv(plist->type == WPLData, NULL);
1340 return WMDataBytes(plist->d.data);
1343 int WMGetPLDataLength(WMPropList * plist)
1345 wassertrv(plist->type == WPLData, 0);
1347 return WMGetDataLength(plist->d.data);
1350 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1352 wassertrv(plist->type == WPLArray, NULL);
1354 return WMGetFromArray(plist->d.array, index);
1357 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1359 wassertrv(plist->type == WPLDictionary, NULL);
1361 return WMHashGet(plist->d.dict, key);
1364 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1366 WMPropList *array, *key;
1367 WMHashEnumerator enumerator;
1369 wassertrv(plist->type == WPLDictionary, NULL);
1371 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1372 array->type = WPLArray;
1373 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1374 array->retainCount = 1;
1376 enumerator = WMEnumerateHashTable(plist->d.dict);
1377 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1378 WMAddToArray(array->d.array, WMRetainPropList(key));
1381 return array;
1384 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1386 WMPropList *ret = NULL;
1387 WMPropList *key, *item;
1388 WMHashEnumerator e;
1389 WMData *data;
1390 int i;
1392 switch (plist->type) {
1393 case WPLString:
1394 ret = WMCreatePLString(plist->d.string);
1395 break;
1396 case WPLData:
1397 data = WMCreateDataWithData(plist->d.data);
1398 ret = WMCreatePLData(data);
1399 WMReleaseData(data);
1400 break;
1401 case WPLArray:
1402 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1403 ret->type = WPLArray;
1404 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1405 ret->retainCount = 1;
1407 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1408 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1410 break;
1411 case WPLDictionary:
1412 ret = WMCreatePLDictionary(NULL, NULL);
1413 e = WMEnumerateHashTable(plist->d.dict);
1414 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1415 WMPutInPLDictionary(ret, key, item);
1417 break;
1418 default:
1419 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1420 wassertrv(False, NULL);
1421 break;
1424 return ret;
1427 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1429 WMPropList *ret = NULL;
1430 WMPropList *key, *item;
1431 WMHashEnumerator e;
1432 WMData *data;
1433 int i;
1435 switch (plist->type) {
1436 case WPLString:
1437 ret = WMCreatePLString(plist->d.string);
1438 break;
1439 case WPLData:
1440 data = WMCreateDataWithData(plist->d.data);
1441 ret = WMCreatePLData(data);
1442 WMReleaseData(data);
1443 break;
1444 case WPLArray:
1445 ret = WMCreatePLArray(NULL);
1446 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1447 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1448 WMAddToArray(ret->d.array, item);
1450 break;
1451 case WPLDictionary:
1452 ret = WMCreatePLDictionary(NULL, NULL);
1453 e = WMEnumerateHashTable(plist->d.dict);
1454 /* While we copy an existing dictionary there is no way that we can
1455 * have duplicate keys, so we don't need to first remove a key/value
1456 * pair before inserting the new key/value.
1458 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1459 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1461 break;
1462 default:
1463 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1464 wassertrv(False, NULL);
1465 break;
1468 return ret;
1471 WMPropList *WMCreatePropListFromDescription(const char *desc)
1473 WMPropList *plist = NULL;
1474 PLData *pldata;
1476 pldata = (PLData *) wmalloc(sizeof(PLData));
1477 pldata->ptr = desc;
1478 pldata->lineNumber = 1;
1480 plist = getPropList(pldata);
1482 if (getNonSpaceChar(pldata) != 0 && plist) {
1483 COMPLAIN(pldata, _("extra data after end of property list"));
1485 * We can't just ignore garbage after the end of the description
1486 * (especially if the description was read from a file), because
1487 * the "garbage" can be the real data and the real garbage is in
1488 * fact in the beginning of the file (which is now inside plist)
1490 WMReleasePropList(plist);
1491 plist = NULL;
1494 wfree(pldata);
1496 return plist;
1499 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1501 return (indented ? indentedDescription(plist, 0) : description(plist));
1504 WMPropList *WMReadPropListFromFile(const char *file)
1506 WMPropList *plist = NULL;
1507 PLData *pldata;
1508 char *read_buf;
1509 FILE *f;
1510 struct stat stbuf;
1511 size_t length;
1513 f = fopen(file, "rb");
1514 if (!f) {
1515 /* let the user print the error message if he really needs to */
1516 /*werror(_("could not open domain file '%s' for reading"), file); */
1517 return NULL;
1520 if (stat(file, &stbuf) == 0) {
1521 length = (size_t) stbuf.st_size;
1522 } else {
1523 werror(_("could not get size for file '%s'"), file);
1524 fclose(f);
1525 return NULL;
1528 read_buf = wmalloc(length + 1);
1529 if (fread(read_buf, length, 1, f) != 1) {
1530 if (ferror(f)) {
1531 werror(_("error reading from file '%s'"), file);
1533 fclose(f);
1534 wfree(read_buf);
1535 return NULL;
1537 read_buf[length] = '\0';
1538 fclose(f);
1540 pldata = (PLData *) wmalloc(sizeof(PLData));
1541 pldata->ptr = read_buf;
1542 pldata->filename = file;
1543 pldata->lineNumber = 1;
1545 plist = getPropList(pldata);
1547 if (getNonSpaceChar(pldata) != 0 && plist) {
1548 COMPLAIN(pldata, _("extra data after end of property list"));
1550 * We can't just ignore garbage after the end of the description
1551 * (especially if the description was read from a file), because
1552 * the "garbage" can be the real data and the real garbage is in
1553 * fact in the beginning of the file (which is now inside plist)
1555 WMReleasePropList(plist);
1556 plist = NULL;
1559 wfree(read_buf);
1560 wfree(pldata);
1562 return plist;
1565 WMPropList *WMReadPropListFromPipe(const char *command)
1567 FILE *file;
1568 WMPropList *plist;
1569 PLData *pldata;
1570 char *read_buf, *read_ptr;
1571 size_t remain_size, line_size;
1572 const size_t block_read_size = 4096;
1573 const size_t block_read_margin = 512;
1575 file = popen(command, "r");
1577 if (!file) {
1578 werror(_("%s:could not open menu file"), command);
1579 return NULL;
1582 /* read from file till EOF or OOM and fill proplist buffer*/
1583 remain_size = block_read_size;
1584 read_buf = wmalloc(remain_size);
1585 read_ptr = read_buf;
1586 while (fgets(read_ptr, remain_size, file) != NULL) {
1587 line_size = strlen(read_ptr);
1589 remain_size -= line_size;
1590 read_ptr += line_size;
1592 if (remain_size < block_read_margin) {
1593 size_t read_length;
1595 read_length = read_ptr - read_buf;
1596 read_buf = wrealloc(read_buf, read_length + block_read_size);
1597 read_ptr = read_buf + read_length;
1598 remain_size = block_read_size;
1602 pclose(file);
1604 pldata = (PLData *) wmalloc(sizeof(PLData));
1605 pldata->ptr = read_buf;
1606 pldata->filename = command;
1607 pldata->lineNumber = 1;
1609 plist = getPropList(pldata);
1611 if (getNonSpaceChar(pldata) != 0 && plist) {
1612 COMPLAIN(pldata, _("extra data after end of property list"));
1614 * We can't just ignore garbage after the end of the description
1615 * (especially if the description was read from a file), because
1616 * the "garbage" can be the real data and the real garbage is in
1617 * fact in the beginning of the file (which is now inside plist)
1619 WMReleasePropList(plist);
1620 plist = NULL;
1623 wfree(read_buf);
1624 wfree(pldata);
1626 return plist;
1629 /* TODO: review this function's code */
1631 Bool WMWritePropListToFile(WMPropList * plist, const char *path)
1633 char *thePath = NULL;
1634 char *desc;
1635 FILE *theFile;
1636 #ifdef HAVE_MKSTEMP
1637 int fd, mask;
1638 #endif
1640 if (!wmkdirhier(path))
1641 return False;
1643 /* Use the path name of the destination file as a prefix for the
1644 * mkstemp() call so that we can be sure that both files are on
1645 * the same filesystem and the subsequent rename() will work. */
1646 thePath = wstrconcat(path, ".XXXXXX");
1648 #ifdef HAVE_MKSTEMP
1650 * We really just want to read the current umask, but as Coverity is
1651 * pointing a possible security issue:
1652 * some versions of mkstemp do not set file rights properly on the
1653 * created file, so it is recommended so set the umask beforehand.
1654 * As we need to set an umask to read the current value, we take this
1655 * opportunity to set a temporary aggresive umask so Coverity won't
1656 * complain, even if we do not really care in the present use case.
1658 mask = umask(S_IRWXG | S_IRWXO);
1659 if ((fd = mkstemp(thePath)) < 0) {
1660 werror(_("mkstemp (%s) failed"), thePath);
1661 goto failure;
1663 umask(mask);
1664 fchmod(fd, 0666 & ~mask);
1665 if ((theFile = fdopen(fd, "wb")) == NULL) {
1666 close(fd);
1668 #else
1669 if (mktemp(thePath) == NULL) {
1670 werror(_("mktemp (%s) failed"), thePath);
1671 goto failure;
1673 theFile = fopen(thePath, "wb");
1674 #endif
1676 if (theFile == NULL) {
1677 werror(_("open (%s) failed"), thePath);
1678 goto failure;
1681 desc = indentedDescription(plist, 0);
1683 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1684 werror(_("writing to file: %s failed"), thePath);
1685 wfree(desc);
1686 goto failure;
1689 wfree(desc);
1691 (void)fsync(fileno(theFile));
1692 if (fclose(theFile) != 0) {
1693 werror(_("fclose (%s) failed"), thePath);
1694 goto failure;
1697 /* If we used a temporary file, we still need to rename() it be the
1698 * real file. Also, we need to try to retain the file attributes of
1699 * the original file we are overwriting (if we are) */
1700 if (rename(thePath, path) != 0) {
1701 werror(_("rename ('%s' to '%s') failed"), thePath, path);
1702 goto failure;
1705 wfree(thePath);
1706 return True;
1708 failure:
1709 unlink(thePath);
1710 wfree(thePath);
1711 return False;
1715 * create a directory hierarchy
1717 * if the last octet of `path' is `/', the full path is
1718 * assumed to be a directory; otherwise path is assumed to be a
1719 * file, and the last component is stripped off. the rest is the
1720 * the hierarchy to be created.
1722 * refuses to create anything outside $GNUSTEP_USER_ROOT
1724 * returns 1 on success, 0 on failure
1726 int wmkdirhier(const char *path)
1728 const char *t;
1729 char *thePath = NULL, buf[1024];
1730 size_t p, plen;
1731 struct stat st;
1733 /* Only create directories under $GNUSTEP_USER_ROOT */
1734 if ((t = wusergnusteppath()) == NULL)
1735 return 0;
1736 if (strncmp(path, t, strlen(t)) != 0)
1737 return 0;
1739 thePath = wstrdup(path);
1740 /* Strip the trailing component if it is a file */
1741 p = strlen(thePath);
1742 while (p && thePath[p] != '/')
1743 thePath[p--] = '\0';
1745 thePath[p] = '\0';
1747 /* Shortcut if it already exists */
1748 if (stat(thePath, &st) == 0) {
1749 wfree(thePath);
1750 if (S_ISDIR(st.st_mode)) {
1751 /* Is a directory alright */
1752 return 1;
1753 } else {
1754 /* Exists, but not a directory, the caller
1755 * might just as well abort now */
1756 return 0;
1760 memset(buf, 0, sizeof(buf));
1761 strncpy(buf, t, sizeof(buf) - 1);
1762 p = strlen(buf);
1763 plen = strlen(thePath);
1765 do {
1766 while (p++ < plen && thePath[p] != '/')
1769 strncpy(buf, thePath, p);
1770 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1771 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1772 werror(_("Could not create component %s"), buf);
1773 wfree(thePath);
1774 return 0;
1776 } while (p < plen);
1778 wfree(thePath);
1779 return 1;
1782 /* ARGSUSED2 */
1783 static int wrmdirhier_fn(const char *path, const struct stat *st,
1784 int type, struct FTW *ftw)
1786 /* Parameter not used, but tell the compiler that it is ok */
1787 (void) st;
1788 (void) ftw;
1790 switch(type) {
1791 case FTW_D:
1792 break;
1793 case FTW_DP:
1794 return rmdir(path);
1795 break;
1796 case FTW_F:
1797 case FTW_SL:
1798 case FTW_SLN:
1799 return unlink(path);
1800 break;
1801 case FTW_DNR:
1802 case FTW_NS:
1803 default:
1804 return EPERM;
1807 /* NOTREACHED */
1808 return 0;
1812 * remove a directory hierarchy
1814 * refuses to remove anything outside $GNUSTEP_USER_ROOT
1816 * returns 1 on success, 0 on failure
1818 * TODO: revisit what's error and what's not
1820 * with inspirations from OpenBSD's bin/rm/rm.c
1822 int wrmdirhier(const char *path)
1824 struct stat st;
1825 int error;
1826 const char *t;
1828 /* Only remove directories under $GNUSTEP_USER_ROOT */
1829 if ((t = wusergnusteppath()) == NULL)
1830 return EPERM;
1831 if (strncmp(path, t, strlen(t)) != 0)
1832 return EPERM;
1834 /* Shortcut if it doesn't exist to begin with */
1835 if (stat(path, &st) == -1)
1836 return ENOENT;
1838 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1840 return error;