Update Serbian translation from master branch
[wmaker-crm.git] / WINGs / proplist.c
blobe7a764dfab4ec692a0c8832b06655b4f33d0838e
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 if (plist)
1257 return (plist->type == WPLString);
1258 else
1259 return False;
1262 Bool WMIsPLData(WMPropList * plist)
1264 if (plist)
1265 return (plist->type == WPLData);
1266 else
1267 return False;
1270 Bool WMIsPLArray(WMPropList * plist)
1272 if (plist)
1273 return (plist->type == WPLArray);
1274 else
1275 return False;
1278 Bool WMIsPLDictionary(WMPropList * plist)
1280 if (plist)
1281 return (plist->type == WPLDictionary);
1282 else
1283 return False;
1286 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1288 WMPropList *key1, *item1, *item2;
1289 WMHashEnumerator enumerator;
1290 int n, i;
1292 if (plist->type != other->type)
1293 return False;
1295 switch (plist->type) {
1296 case WPLString:
1297 if (caseSensitive) {
1298 return (strcmp(plist->d.string, other->d.string) == 0);
1299 } else {
1300 return (strcasecmp(plist->d.string, other->d.string) == 0);
1302 case WPLData:
1303 return WMIsDataEqualToData(plist->d.data, other->d.data);
1304 case WPLArray:
1305 n = WMGetArrayItemCount(plist->d.array);
1306 if (n != WMGetArrayItemCount(other->d.array))
1307 return False;
1308 for (i = 0; i < n; i++) {
1309 item1 = WMGetFromArray(plist->d.array, i);
1310 item2 = WMGetFromArray(other->d.array, i);
1311 if (!WMIsPropListEqualTo(item1, item2))
1312 return False;
1314 return True;
1315 case WPLDictionary:
1316 if (WMCountHashTable(plist->d.dict) != WMCountHashTable(other->d.dict))
1317 return False;
1318 enumerator = WMEnumerateHashTable(plist->d.dict);
1319 while (WMNextHashEnumeratorItemAndKey(&enumerator, (void **)&item1, (void **)&key1)) {
1320 item2 = WMHashGet(other->d.dict, key1);
1321 if (!item2 || !item1 || !WMIsPropListEqualTo(item1, item2))
1322 return False;
1324 return True;
1325 default:
1326 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1327 wassertrv(False, False);
1328 break;
1331 return False;
1334 char *WMGetFromPLString(WMPropList * plist)
1336 wassertrv(plist->type == WPLString, NULL);
1338 return plist->d.string;
1341 WMData *WMGetFromPLData(WMPropList * plist)
1343 wassertrv(plist->type == WPLData, NULL);
1345 return plist->d.data;
1348 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1350 wassertrv(plist->type == WPLData, NULL);
1352 return WMDataBytes(plist->d.data);
1355 int WMGetPLDataLength(WMPropList * plist)
1357 wassertrv(plist->type == WPLData, 0);
1359 return WMGetDataLength(plist->d.data);
1362 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1364 wassertrv(plist->type == WPLArray, NULL);
1366 return WMGetFromArray(plist->d.array, index);
1369 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1371 wassertrv(plist->type == WPLDictionary, NULL);
1373 return WMHashGet(plist->d.dict, key);
1376 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1378 WMPropList *array, *key;
1379 WMHashEnumerator enumerator;
1381 wassertrv(plist->type == WPLDictionary, NULL);
1383 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1384 array->type = WPLArray;
1385 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1386 array->retainCount = 1;
1388 enumerator = WMEnumerateHashTable(plist->d.dict);
1389 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1390 WMAddToArray(array->d.array, WMRetainPropList(key));
1393 return array;
1396 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1398 WMPropList *ret = NULL;
1399 WMPropList *key, *item;
1400 WMHashEnumerator e;
1401 WMData *data;
1402 int i;
1404 switch (plist->type) {
1405 case WPLString:
1406 ret = WMCreatePLString(plist->d.string);
1407 break;
1408 case WPLData:
1409 data = WMCreateDataWithData(plist->d.data);
1410 ret = WMCreatePLData(data);
1411 WMReleaseData(data);
1412 break;
1413 case WPLArray:
1414 ret = (WMPropList *) wmalloc(sizeof(W_PropList));
1415 ret->type = WPLArray;
1416 ret->d.array = WMCreateArrayWithArray(plist->d.array);
1417 ret->retainCount = 1;
1419 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1420 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1422 break;
1423 case WPLDictionary:
1424 ret = WMCreatePLDictionary(NULL, NULL);
1425 e = WMEnumerateHashTable(plist->d.dict);
1426 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1427 WMPutInPLDictionary(ret, key, item);
1429 break;
1430 default:
1431 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1432 wassertrv(False, NULL);
1433 break;
1436 return ret;
1439 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1441 WMPropList *ret = NULL;
1442 WMPropList *key, *item;
1443 WMHashEnumerator e;
1444 WMData *data;
1445 int i;
1447 switch (plist->type) {
1448 case WPLString:
1449 ret = WMCreatePLString(plist->d.string);
1450 break;
1451 case WPLData:
1452 data = WMCreateDataWithData(plist->d.data);
1453 ret = WMCreatePLData(data);
1454 WMReleaseData(data);
1455 break;
1456 case WPLArray:
1457 ret = WMCreatePLArray(NULL);
1458 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1459 item = WMDeepCopyPropList(WMGetFromArray(plist->d.array, i));
1460 WMAddToArray(ret->d.array, item);
1462 break;
1463 case WPLDictionary:
1464 ret = WMCreatePLDictionary(NULL, NULL);
1465 e = WMEnumerateHashTable(plist->d.dict);
1466 /* While we copy an existing dictionary there is no way that we can
1467 * have duplicate keys, so we don't need to first remove a key/value
1468 * pair before inserting the new key/value.
1470 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1471 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1473 break;
1474 default:
1475 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1476 wassertrv(False, NULL);
1477 break;
1480 return ret;
1483 WMPropList *WMCreatePropListFromDescription(const char *desc)
1485 WMPropList *plist = NULL;
1486 PLData *pldata;
1488 pldata = (PLData *) wmalloc(sizeof(PLData));
1489 pldata->ptr = desc;
1490 pldata->lineNumber = 1;
1492 plist = getPropList(pldata);
1494 if (getNonSpaceChar(pldata) != 0 && plist) {
1495 COMPLAIN(pldata, _("extra data after end of property list"));
1497 * We can't just ignore garbage after the end of the description
1498 * (especially if the description was read from a file), because
1499 * the "garbage" can be the real data and the real garbage is in
1500 * fact in the beginning of the file (which is now inside plist)
1502 WMReleasePropList(plist);
1503 plist = NULL;
1506 wfree(pldata);
1508 return plist;
1511 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1513 return (indented ? indentedDescription(plist, 0) : description(plist));
1516 WMPropList *WMReadPropListFromFile(const char *file)
1518 WMPropList *plist = NULL;
1519 PLData *pldata;
1520 char *read_buf;
1521 FILE *f;
1522 struct stat stbuf;
1523 size_t length;
1525 f = fopen(file, "rb");
1526 if (!f) {
1527 /* let the user print the error message if he really needs to */
1528 /*werror(_("could not open domain file '%s' for reading"), file); */
1529 return NULL;
1532 if (stat(file, &stbuf) == 0) {
1533 length = (size_t) stbuf.st_size;
1534 } else {
1535 werror(_("could not get size for file '%s'"), file);
1536 fclose(f);
1537 return NULL;
1540 read_buf = wmalloc(length + 1);
1541 if (fread(read_buf, length, 1, f) != 1) {
1542 if (ferror(f)) {
1543 werror(_("error reading from file '%s'"), file);
1545 fclose(f);
1546 wfree(read_buf);
1547 return NULL;
1549 read_buf[length] = '\0';
1550 fclose(f);
1552 pldata = (PLData *) wmalloc(sizeof(PLData));
1553 pldata->ptr = read_buf;
1554 pldata->filename = file;
1555 pldata->lineNumber = 1;
1557 plist = getPropList(pldata);
1559 if (getNonSpaceChar(pldata) != 0 && plist) {
1560 COMPLAIN(pldata, _("extra data after end of property list"));
1562 * We can't just ignore garbage after the end of the description
1563 * (especially if the description was read from a file), because
1564 * the "garbage" can be the real data and the real garbage is in
1565 * fact in the beginning of the file (which is now inside plist)
1567 WMReleasePropList(plist);
1568 plist = NULL;
1571 wfree(read_buf);
1572 wfree(pldata);
1574 return plist;
1577 WMPropList *WMReadPropListFromPipe(const char *command)
1579 FILE *file;
1580 WMPropList *plist;
1581 PLData *pldata;
1582 char *read_buf, *read_ptr;
1583 size_t remain_size, line_size;
1584 const size_t block_read_size = 4096;
1585 const size_t block_read_margin = 512;
1587 file = popen(command, "r");
1589 if (!file) {
1590 werror(_("%s:could not open menu file"), command);
1591 return NULL;
1594 /* read from file till EOF or OOM and fill proplist buffer*/
1595 remain_size = block_read_size;
1596 read_buf = wmalloc(remain_size);
1597 read_ptr = read_buf;
1598 while (fgets(read_ptr, remain_size, file) != NULL) {
1599 line_size = strlen(read_ptr);
1601 remain_size -= line_size;
1602 read_ptr += line_size;
1604 if (remain_size < block_read_margin) {
1605 size_t read_length;
1607 read_length = read_ptr - read_buf;
1608 read_buf = wrealloc(read_buf, read_length + block_read_size);
1609 read_ptr = read_buf + read_length;
1610 remain_size = block_read_size;
1614 pclose(file);
1616 pldata = (PLData *) wmalloc(sizeof(PLData));
1617 pldata->ptr = read_buf;
1618 pldata->filename = command;
1619 pldata->lineNumber = 1;
1621 plist = getPropList(pldata);
1623 if (getNonSpaceChar(pldata) != 0 && plist) {
1624 COMPLAIN(pldata, _("extra data after end of property list"));
1626 * We can't just ignore garbage after the end of the description
1627 * (especially if the description was read from a file), because
1628 * the "garbage" can be the real data and the real garbage is in
1629 * fact in the beginning of the file (which is now inside plist)
1631 WMReleasePropList(plist);
1632 plist = NULL;
1635 wfree(read_buf);
1636 wfree(pldata);
1638 return plist;
1641 /* TODO: review this function's code */
1643 Bool WMWritePropListToFile(WMPropList * plist, const char *path)
1645 char *thePath = NULL;
1646 char *desc;
1647 FILE *theFile;
1648 #ifdef HAVE_MKSTEMP
1649 int fd, mask;
1650 #endif
1652 if (!wmkdirhier(path))
1653 return False;
1655 /* Use the path name of the destination file as a prefix for the
1656 * mkstemp() call so that we can be sure that both files are on
1657 * the same filesystem and the subsequent rename() will work. */
1658 thePath = wstrconcat(path, ".XXXXXX");
1660 #ifdef HAVE_MKSTEMP
1662 * We really just want to read the current umask, but as Coverity is
1663 * pointing a possible security issue:
1664 * some versions of mkstemp do not set file rights properly on the
1665 * created file, so it is recommended so set the umask beforehand.
1666 * As we need to set an umask to read the current value, we take this
1667 * opportunity to set a temporary aggresive umask so Coverity won't
1668 * complain, even if we do not really care in the present use case.
1670 mask = umask(S_IRWXG | S_IRWXO);
1671 if ((fd = mkstemp(thePath)) < 0) {
1672 werror(_("mkstemp (%s) failed"), thePath);
1673 goto failure;
1675 umask(mask);
1676 fchmod(fd, 0666 & ~mask);
1677 if ((theFile = fdopen(fd, "wb")) == NULL) {
1678 close(fd);
1680 #else
1681 if (mktemp(thePath) == NULL) {
1682 werror(_("mktemp (%s) failed"), thePath);
1683 goto failure;
1685 theFile = fopen(thePath, "wb");
1686 #endif
1688 if (theFile == NULL) {
1689 werror(_("open (%s) failed"), thePath);
1690 goto failure;
1693 desc = indentedDescription(plist, 0);
1695 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1696 werror(_("writing to file: %s failed"), thePath);
1697 wfree(desc);
1698 fclose(theFile);
1699 goto failure;
1702 wfree(desc);
1704 (void)fsync(fileno(theFile));
1705 if (fclose(theFile) != 0) {
1706 werror(_("fclose (%s) failed"), thePath);
1707 goto failure;
1710 /* If we used a temporary file, we still need to rename() it be the
1711 * real file. Also, we need to try to retain the file attributes of
1712 * the original file we are overwriting (if we are) */
1713 if (rename(thePath, path) != 0) {
1714 werror(_("rename ('%s' to '%s') failed"), thePath, path);
1715 goto failure;
1718 wfree(thePath);
1719 return True;
1721 failure:
1722 unlink(thePath);
1723 wfree(thePath);
1724 return False;
1728 * create a directory hierarchy
1730 * if the last octet of `path' is `/', the full path is
1731 * assumed to be a directory; otherwise path is assumed to be a
1732 * file, and the last component is stripped off. the rest is the
1733 * the hierarchy to be created.
1735 * refuses to create anything outside $WMAKER_USER_ROOT
1737 * returns 1 on success, 0 on failure
1739 int wmkdirhier(const char *path)
1741 const char *t;
1742 char *thePath = NULL, buf[1024];
1743 size_t p, plen;
1744 struct stat st;
1746 /* Only create directories under $WMAKER_USER_ROOT */
1747 if ((t = wusergnusteppath()) == NULL)
1748 return 0;
1749 if (strncmp(path, t, strlen(t)) != 0)
1750 return 0;
1752 thePath = wstrdup(path);
1753 /* Strip the trailing component if it is a file */
1754 p = strlen(thePath);
1755 while (p && thePath[p] != '/')
1756 thePath[p--] = '\0';
1758 thePath[p] = '\0';
1760 /* Shortcut if it already exists */
1761 if (stat(thePath, &st) == 0) {
1762 wfree(thePath);
1763 if (S_ISDIR(st.st_mode)) {
1764 /* Is a directory alright */
1765 return 1;
1766 } else {
1767 /* Exists, but not a directory, the caller
1768 * might just as well abort now */
1769 return 0;
1773 memset(buf, 0, sizeof(buf));
1774 strncpy(buf, t, sizeof(buf) - 1);
1775 p = strlen(buf);
1776 plen = strlen(thePath);
1778 do {
1779 while (p++ < plen && thePath[p] != '/')
1782 strncpy(buf, thePath, p);
1783 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1784 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1785 werror(_("Could not create component %s"), buf);
1786 wfree(thePath);
1787 return 0;
1789 } while (p < plen);
1791 wfree(thePath);
1792 return 1;
1795 /* ARGSUSED2 */
1796 static int wrmdirhier_fn(const char *path, const struct stat *st,
1797 int type, struct FTW *ftw)
1799 /* Parameter not used, but tell the compiler that it is ok */
1800 (void) st;
1801 (void) ftw;
1803 switch(type) {
1804 case FTW_D:
1805 break;
1806 case FTW_DP:
1807 return rmdir(path);
1808 break;
1809 case FTW_F:
1810 case FTW_SL:
1811 case FTW_SLN:
1812 return unlink(path);
1813 break;
1814 case FTW_DNR:
1815 case FTW_NS:
1816 default:
1817 return EPERM;
1820 /* NOTREACHED */
1821 return 0;
1825 * remove a directory hierarchy
1827 * refuses to remove anything outside $WMAKER_USER_ROOT/Defaults or $WMAKER_USER_ROOT/Library
1829 * returns 1 on success, 0 on failure
1831 * TODO: revisit what's error and what's not
1833 * with inspirations from OpenBSD's bin/rm/rm.c
1835 int wrmdirhier(const char *path)
1837 const char *libpath;
1838 char *udefpath = NULL;
1839 struct stat st;
1840 int error;
1842 /* Only remove directories under $WMAKER_USER_ROOT/Defaults or $WMAKER_USER_ROOT/Library */
1843 libpath = wuserdatapath();
1844 if (strncmp(path, libpath, strlen(libpath)) == 0)
1845 if (path[strlen(libpath)] == '/')
1846 goto path_in_valid_tree;
1848 udefpath = wdefaultspathfordomain("");
1849 if (strncmp(path, udefpath, strlen(udefpath)) == 0)
1850 /* Note: by side effect, 'udefpath' already contains a final '/' */
1851 goto path_in_valid_tree;
1853 wfree(udefpath);
1854 return EPERM;
1856 path_in_valid_tree:
1857 wfree(udefpath);
1859 /* Shortcut if it doesn't exist to begin with */
1860 if (stat(path, &st) == -1)
1861 return ENOENT;
1863 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1865 return error;