1
2 #include <sys/types.h>
3 #include <sys/stat.h>
4
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>
14
15 #include "WUtil.h"
16 #include "wconfig.h"
17
18 typedef enum {
19 WPLString = 0x57504c01,
20 WPLData = 0x57504c02,
21 WPLArray = 0x57504c03,
22 WPLDictionary = 0x57504c04
23 } WPLType;
24
25 typedef struct W_PropList {
26 WPLType type;
27
28 union {
29 char *string;
30 WMData *data;
31 WMArray *array;
32 WMHashTable *dict;
33 } d;
34
35 int retainCount;
36 } W_PropList;
37
38 typedef struct PLData {
39 const char *ptr;
40 int pos;
41 const char *filename;
42 int lineNumber;
43 } PLData;
44
45 typedef struct StringBuffer {
46 char *str;
47 int size;
48 } StringBuffer;
49
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);
57
58 typedef Bool(*isEqualFunc) (const void *, const void *);
59
60 static const WMHashTableCallbacks WMPropListHashCallbacks = {
61 hashPropList,
62 (isEqualFunc) WMIsPropListEqualTo,
63 NULL,
64 NULL
65 };
66
67 static Bool caseSensitive = True;
68
69 #define BUFFERSIZE 8192
70 #define BUFFERSIZE_INCREMENT 1024
71
72 #if 0
73 # define DPUT(s) puts(s)
74 #else
75 # define DPUT(s)
76 #endif
77
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)
82
83 #define ISSTRINGABLE(c) (isalnum(c) || (c)=='.' || (c)=='_' || (c)=='/' \
84 || (c)=='+')
85
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);\
90 }
91
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))
99
100 #define MaxHashLength 64
101
hashPropList(const void * param)102 static unsigned hashPropList(const void *param)
103 {
104 WMPropList *plist= (WMPropList *) param;
105 unsigned ret = 0;
106 unsigned ctr = 0;
107 const char *key;
108 int i, len;
109
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 *);
117 }
118 /*while (*key) {
119 ret ^= tolower(*key++) << ctr;
120 ctr = (ctr + 1) % sizeof (char *);
121 } */
122 break;
123
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 *);
130 }
131 break;
132
133 default:
134 wwarning(_("Only string or data is supported for a proplist dictionary key"));
135 wassertrv(False, 0);
136 break;
137 }
138
139 return ret;
140 }
141
retainPropListByCount(WMPropList * plist,int count)142 static WMPropList *retainPropListByCount(WMPropList * plist, int count)
143 {
144 WMPropList *key, *value;
145 WMHashEnumerator e;
146 int i;
147
148 plist->retainCount += count;
149
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);
157 }
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);
164 }
165 break;
166 default:
167 wwarning(_("Used proplist functions on non-WMPropLists objects"));
168 wassertrv(False, NULL);
169 break;
170 }
171
172 return plist;
173 }
174
releasePropListByCount(WMPropList * plist,int count)175 static void releasePropListByCount(WMPropList * plist, int count)
176 {
177 WMPropList *key, *value;
178 WMHashEnumerator e;
179 int i;
180
181 plist->retainCount -= count;
182
183 switch (plist->type) {
184 case WPLString:
185 if (plist->retainCount < 1) {
186 wfree(plist->d.string);
187 wfree(plist);
188 }
189 break;
190 case WPLData:
191 if (plist->retainCount < 1) {
192 WMReleaseData(plist->d.data);
193 wfree(plist);
194 }
195 break;
196 case WPLArray:
197 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
198 releasePropListByCount(WMGetFromArray(plist->d.array, i), count);
199 }
200 if (plist->retainCount < 1) {
201 WMFreeArray(plist->d.array);
202 wfree(plist);
203 }
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);
210 }
211 if (plist->retainCount < 1) {
212 WMFreeHashTable(plist->d.dict);
213 wfree(plist);
214 }
215 break;
216 default:
217 wwarning(_("Used proplist functions on non-WMPropLists objects"));
218 wassertr(False);
219 break;
220 }
221 }
222
dataDescription(WMPropList * plist)223 static char *dataDescription(WMPropList * plist)
224 {
225 const unsigned char *data;
226 char *retVal;
227 int i, j, length;
228
229 data = WMDataBytes(plist->d.data);
230 length = WMGetDataLength(plist->d.data);
231
232 retVal = (char *)wmalloc(2 * length + length / 4 + 3);
233
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++] = ' ';
241 }
242 }
243 retVal[j++] = '>';
244 retVal[j] = '\0';
245
246 return retVal;
247 }
248
stringDescription(WMPropList * plist)249 static char *stringDescription(WMPropList * plist)
250 {
251 const char *str;
252 char *retVal, *sPtr, *dPtr;
253 int len, quote;
254 unsigned char ch;
255
256 str = plist->d.string;
257
258 if (strlen(str) == 0) {
259 return wstrdup("\"\"");
260 }
261
262 /* FIXME: make this work with unichars. */
263
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;
274 }
275 sPtr++;
276 len++;
277 }
278
279 if (quote)
280 len += 2;
281
282 retVal = (char *)wmalloc(len + 1);
283
284 sPtr = (char *)str;
285 dPtr = retVal;
286
287 if (quote)
288 *dPtr++ = '"';
289
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 \ */
314 }
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;
322 }
323 sPtr++;
324 dPtr++;
325 }
326
327 if (quote)
328 *dPtr++ = '"';
329
330 *dPtr = '\0';
331
332 return retVal;
333 }
334
description(WMPropList * plist)335 static char *description(WMPropList * plist)
336 {
337 WMPropList *key, *val;
338 char *retstr = NULL;
339 char *str, *tmp, *skey, *sval;
340 WMHashEnumerator e;
341 int i;
342
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;
361 }
362 wfree(str);
363 }
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;
378 }
379 retstr = wstrappend(retstr, "}");
380 break;
381 default:
382 wwarning(_("Used proplist functions on non-WMPropLists objects"));
383 wassertrv(False, NULL);
384 break;
385 }
386
387 return retstr;
388 }
389
indentedDescription(WMPropList * plist,int level)390 static char *indentedDescription(WMPropList * plist, int level)
391 {
392 WMPropList *key, *val;
393 char *retstr = NULL;
394 char *str, *tmp, *skey, *sval;
395 WMHashEnumerator e;
396 int i;
397
398 if (plist->type == WPLArray /* || plist->type==WPLDictionary */ ) {
399 retstr = description(plist);
400
401 if (retstr && ((2 * (level + 1) + strlen(retstr)) <= 77)) {
402 return retstr;
403 } else if (retstr) {
404 wfree(retstr);
405 retstr = NULL;
406 }
407 }
408
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;
430 }
431 wfree(str);
432 }
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;
451 }
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;
461 }
462
463 return retstr;
464 }
465
getChar(PLData * pldata)466 static inline int getChar(PLData * pldata)
467 {
468 int c;
469
470 c = pldata->ptr[pldata->pos];
471 if (c == 0) {
472 return 0;
473 }
474
475 pldata->pos++;
476
477 if (c == '\n')
478 pldata->lineNumber++;
479
480 return c;
481 }
482
getNonSpaceChar(PLData * pldata)483 static inline int getNonSpaceChar(PLData * pldata)
484 {
485 int c;
486
487 while (1) {
488 c = pldata->ptr[pldata->pos];
489 if (c == 0) {
490 break;
491 }
492 pldata->pos++;
493 if (c == '\n') {
494 pldata->lineNumber++;
495 } else if (!isspace(c)) {
496 break;
497 }
498 }
499
500 return c;
501 }
502
unescapestr(const char * src)503 static char *unescapestr(const char *src)
504 {
505 char *dest = wmalloc(strlen(src) + 1);
506 char *dPtr;
507 char ch;
508
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;
522
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);
533 }
534 }
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;
561 }
562 }
563 }
564 }
565
566 *dPtr = 0;
567
568 return dest;
569 }
570
getPLString(PLData * pldata)571 static WMPropList *getPLString(PLData * pldata)
572 {
573 WMPropList *plist;
574 StringBuffer sBuf;
575 int ptr = 0;
576 int c;
577
578 sBuf.str = wmalloc(BUFFERSIZE);
579 sBuf.size = BUFFERSIZE;
580
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--;
589 }
590 break;
591 }
592 }
593
594 sBuf.str[ptr] = 0;
595
596 if (ptr == 0) {
597 plist = NULL;
598 } else {
599 char *tmp = unescapestr(sBuf.str);
600 plist = WMCreatePLString(tmp);
601 wfree(tmp);
602 }
603
604 wfree(sBuf.str);
605
606 return plist;
607 }
608
getPLQString(PLData * pldata)609 static WMPropList *getPLQString(PLData * pldata)
610 {
611 WMPropList *plist;
612 int ptr = 0, escaping = 0, ok = 1;
613 int c;
614 StringBuffer sBuf;
615
616 sBuf.str = wmalloc(BUFFERSIZE);
617 sBuf.size = BUFFERSIZE;
618
619 while (1) {
620 c = getChar(pldata);
621 if (!escaping) {
622 if (c == '\\') {
623 escaping = 1;
624 continue;
625 } else if (c == '"') {
626 break;
627 }
628 } else {
629 CHECK_BUFFER_SIZE(sBuf, ptr);
630 sBuf.str[ptr++] = '\\';
631 escaping = 0;
632 }
633
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;
641 }
642 }
643
644 sBuf.str[ptr] = 0;
645
646 if (!ok) {
647 plist = NULL;
648 } else {
649 char *tmp = unescapestr(sBuf.str);
650 plist = WMCreatePLString(tmp);
651 wfree(tmp);
652 }
653
654 wfree(sBuf.str);
655
656 return plist;
657 }
658
getPLData(PLData * pldata)659 static WMPropList *getPLData(PLData * pldata)
660 {
661 int ok = 1;
662 int len = 0;
663 int c1, c2;
664 unsigned char buf[BUFFERSIZE], byte;
665 WMPropList *plist;
666 WMData *data;
667
668 data = WMCreateDataWithCapacity(0);
669
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;
691 }
692 } else {
693 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
694 ok = 0;
695 break;
696 }
697 } else {
698 COMPLAIN(pldata, _("non hexdigit character in PropList data"));
699 ok = 0;
700 break;
701 }
702 }
703
704 if (!ok) {
705 WMReleaseData(data);
706 return NULL;
707 }
708
709 if (len > 0)
710 WMAppendDataBytes(data, buf, len);
711
712 plist = WMCreatePLData(data);
713 WMReleaseData(data);
714
715 return plist;
716 }
717
getPLArray(PLData * pldata)718 static WMPropList *getPLArray(PLData * pldata)
719 {
720 Bool first = True;
721 int ok = 1;
722 int c;
723 WMPropList *array, *obj;
724
725 array = WMCreatePLArray(NULL);
726
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--;
743 }
744 first = False;
745
746 obj = getPropList(pldata);
747 if (!obj) {
748 COMPLAIN(pldata, _("could not get PropList array element"));
749 ok = 0;
750 break;
751 }
752 WMAddToPLArray(array, obj);
753 WMReleasePropList(obj);
754 }
755
756 if (!ok) {
757 WMReleasePropList(array);
758 array = NULL;
759 }
760
761 return array;
762 }
763
getPLDictionary(PLData * pldata)764 static WMPropList *getPLDictionary(PLData * pldata)
765 {
766 int ok = 1;
767 int c;
768 WMPropList *dict, *key, *value;
769
770 dict = WMCreatePLDictionary(NULL, NULL);
771
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;
780 }
781
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"));
796 }
797 ok = 0;
798 break;
799 }
800
801 if (!key) {
802 COMPLAIN(pldata, _("error parsing PropList dictionary key"));
803 ok = 0;
804 break;
805 }
806
807 c = getNonSpaceChar(pldata);
808 if (c != '=') {
809 WMReleasePropList(key);
810 COMPLAIN(pldata, _("missing = in PropList dictionary entry"));
811 ok = 0;
812 break;
813 }
814
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;
822 }
823
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;
831 }
832
833 WMPutInPLDictionary(dict, key, value);
834 WMReleasePropList(key);
835 WMReleasePropList(value);
836 }
837
838 if (!ok) {
839 WMReleasePropList(dict);
840 dict = NULL;
841 }
842
843 return dict;
844 }
845
getPropList(PLData * pldata)846 static WMPropList *getPropList(PLData * pldata)
847 {
848 WMPropList *plist;
849 int c;
850
851 c = getNonSpaceChar(pldata);
852
853 switch (c) {
854 case 0:
855 DPUT("End of PropList");
856 plist = NULL;
857 break;
858
859 case '{':
860 DPUT("Getting PropList dictionary");
861 plist = getPLDictionary(pldata);
862 break;
863
864 case '(':
865 DPUT("Getting PropList array");
866 plist = getPLArray(pldata);
867 break;
868
869 case '<':
870 DPUT("Getting PropList data");
871 plist = getPLData(pldata);
872 break;
873
874 case '"':
875 DPUT("Getting PropList quoted string");
876 plist = getPLQString(pldata);
877 break;
878
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."));
889 }
890 plist = NULL;
891 }
892 break;
893 }
894
895 return plist;
896 }
897
WMPLSetCaseSensitive(Bool caseSensitiveness)898 void WMPLSetCaseSensitive(Bool caseSensitiveness)
899 {
900 caseSensitive = caseSensitiveness;
901 }
902
WMCreatePLString(const char * str)903 WMPropList *WMCreatePLString(const char *str)
904 {
905 WMPropList *plist;
906
907 wassertrv(str != NULL, NULL);
908
909 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
910 plist->type = WPLString;
911 plist->d.string = wstrdup(str);
912 plist->retainCount = 1;
913
914 return plist;
915 }
916
WMCreatePLData(WMData * data)917 WMPropList *WMCreatePLData(WMData * data)
918 {
919 WMPropList *plist;
920
921 wassertrv(data != NULL, NULL);
922
923 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
924 plist->type = WPLData;
925 plist->d.data = WMRetainData(data);
926 plist->retainCount = 1;
927
928 return plist;
929 }
930
WMCreatePLDataWithBytes(const unsigned char * bytes,unsigned int length)931 WMPropList *WMCreatePLDataWithBytes(const unsigned char *bytes, unsigned int length)
932 {
933 WMPropList *plist;
934
935 wassertrv(bytes != NULL, NULL);
936
937 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
938 plist->type = WPLData;
939 plist->d.data = WMCreateDataWithBytes(bytes, length);
940 plist->retainCount = 1;
941
942 return plist;
943 }
944
WMCreatePLDataWithBytesNoCopy(unsigned char * bytes,unsigned int length,WMFreeDataProc * destructor)945 WMPropList *WMCreatePLDataWithBytesNoCopy(unsigned char *bytes, unsigned int length, WMFreeDataProc * destructor)
946 {
947 WMPropList *plist;
948
949 wassertrv(bytes != NULL, NULL);
950
951 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
952 plist->type = WPLData;
953 plist->d.data = WMCreateDataWithBytesNoCopy(bytes, length, destructor);
954 plist->retainCount = 1;
955
956 return plist;
957 }
958
WMCreatePLArray(WMPropList * elem,...)959 WMPropList *WMCreatePLArray(WMPropList * elem, ...)
960 {
961 WMPropList *plist, *nelem;
962 va_list ap;
963
964 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
965 plist->type = WPLArray;
966 plist->d.array = WMCreateArray(4);
967 plist->retainCount = 1;
968
969 if (!elem)
970 return plist;
971
972 WMAddToArray(plist->d.array, WMRetainPropList(elem));
973
974 va_start(ap, elem);
975
976 while (1) {
977 nelem = va_arg(ap, WMPropList *);
978 if (!nelem) {
979 va_end(ap);
980 return plist;
981 }
982 WMAddToArray(plist->d.array, WMRetainPropList(nelem));
983 }
984 }
985
WMCreatePLDictionary(WMPropList * key,WMPropList * value,...)986 WMPropList *WMCreatePLDictionary(WMPropList * key, WMPropList * value, ...)
987 {
988 WMPropList *plist, *nkey, *nvalue, *k, *v;
989 va_list ap;
990
991 plist = (WMPropList *) wmalloc(sizeof(W_PropList));
992 plist->type = WPLDictionary;
993 plist->d.dict = WMCreateHashTable(WMPropListHashCallbacks);
994 plist->retainCount = 1;
995
996 if (!key || !value)
997 return plist;
998
999 WMHashInsert(plist->d.dict, WMRetainPropList(key), WMRetainPropList(value));
1000
1001 va_start(ap, value);
1002
1003 while (1) {
1004 nkey = va_arg(ap, WMPropList *);
1005 if (!nkey) {
1006 va_end(ap);
1007 return plist;
1008 }
1009 nvalue = va_arg(ap, WMPropList *);
1010 if (!nvalue) {
1011 va_end(ap);
1012 return plist;
1013 }
1014 if (WMHashGetItemAndKey(plist->d.dict, nkey, (void **)&v, (void **)&k)) {
1015 WMHashRemove(plist->d.dict, k);
1016 WMReleasePropList(k);
1017 WMReleasePropList(v);
1018 }
1019 WMHashInsert(plist->d.dict, WMRetainPropList(nkey), WMRetainPropList(nvalue));
1020 }
1021 }
1022
WMRetainPropList(WMPropList * plist)1023 WMPropList *WMRetainPropList(WMPropList * plist)
1024 {
1025 WMPropList *key, *value;
1026 WMHashEnumerator e;
1027 int i;
1028
1029 plist->retainCount++;
1030
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));
1038 }
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);
1045 }
1046 break;
1047 default:
1048 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1049 wassertrv(False, NULL);
1050 break;
1051 }
1052
1053 return plist;
1054 }
1055
WMReleasePropList(WMPropList * plist)1056 void WMReleasePropList(WMPropList * plist)
1057 {
1058 WMPropList *key, *value;
1059 WMHashEnumerator e;
1060 int i;
1061
1062 plist->retainCount--;
1063
1064 switch (plist->type) {
1065 case WPLString:
1066 if (plist->retainCount < 1) {
1067 wfree(plist->d.string);
1068 wfree(plist);
1069 }
1070 break;
1071 case WPLData:
1072 if (plist->retainCount < 1) {
1073 WMReleaseData(plist->d.data);
1074 wfree(plist);
1075 }
1076 break;
1077 case WPLArray:
1078 for (i = 0; i < WMGetArrayItemCount(plist->d.array); i++) {
1079 WMReleasePropList(WMGetFromArray(plist->d.array, i));
1080 }
1081 if (plist->retainCount < 1) {
1082 WMFreeArray(plist->d.array);
1083 wfree(plist);
1084 }
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);
1091 }
1092 if (plist->retainCount < 1) {
1093 WMFreeHashTable(plist->d.dict);
1094 wfree(plist);
1095 }
1096 break;
1097 default:
1098 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1099 wassertr(False);
1100 break;
1101 }
1102 }
1103
WMInsertInPLArray(WMPropList * plist,int index,WMPropList * item)1104 void WMInsertInPLArray(WMPropList * plist, int index, WMPropList * item)
1105 {
1106 wassertr(plist->type == WPLArray);
1107
1108 retainPropListByCount(item, plist->retainCount);
1109 WMInsertInArray(plist->d.array, index, item);
1110 }
1111
WMAddToPLArray(WMPropList * plist,WMPropList * item)1112 void WMAddToPLArray(WMPropList * plist, WMPropList * item)
1113 {
1114 wassertr(plist->type == WPLArray);
1115
1116 retainPropListByCount(item, plist->retainCount);
1117 WMAddToArray(plist->d.array, item);
1118 }
1119
WMDeleteFromPLArray(WMPropList * plist,int index)1120 void WMDeleteFromPLArray(WMPropList * plist, int index)
1121 {
1122 WMPropList *item;
1123
1124 wassertr(plist->type == WPLArray);
1125
1126 item = WMGetFromArray(plist->d.array, index);
1127 if (item != NULL) {
1128 WMDeleteFromArray(plist->d.array, index);
1129 releasePropListByCount(item, plist->retainCount);
1130 }
1131 }
1132
WMRemoveFromPLArray(WMPropList * plist,WMPropList * item)1133 void WMRemoveFromPLArray(WMPropList * plist, WMPropList * item)
1134 {
1135 WMPropList *iPtr;
1136 int i;
1137
1138 wassertr(plist->type == WPLArray);
1139
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;
1146 }
1147 }
1148 }
1149
WMPutInPLDictionary(WMPropList * plist,WMPropList * key,WMPropList * value)1150 void WMPutInPLDictionary(WMPropList * plist, WMPropList * key, WMPropList * value)
1151 {
1152 wassertr(plist->type == WPLDictionary);
1153
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); */
1160 }
1161
WMRemoveFromPLDictionary(WMPropList * plist,WMPropList * key)1162 void WMRemoveFromPLDictionary(WMPropList * plist, WMPropList * key)
1163 {
1164 WMPropList *k, *v;
1165
1166 wassertr(plist->type == WPLDictionary);
1167
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);
1172 }
1173 }
1174
WMMergePLDictionaries(WMPropList * dest,WMPropList * source,Bool recursive)1175 WMPropList *WMMergePLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1176 {
1177 WMPropList *key, *value, *dvalue;
1178 WMHashEnumerator e;
1179
1180 wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
1181
1182 if (source == dest)
1183 return dest;
1184
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);
1193 }
1194 } else {
1195 WMPutInPLDictionary(dest, key, value);
1196 }
1197 }
1198
1199 return dest;
1200 }
1201
WMSubtractPLDictionaries(WMPropList * dest,WMPropList * source,Bool recursive)1202 WMPropList *WMSubtractPLDictionaries(WMPropList * dest, WMPropList * source, Bool recursive)
1203 {
1204 WMPropList *key, *value, *dvalue;
1205 WMHashEnumerator e;
1206
1207 wassertrv(source->type == WPLDictionary && dest->type == WPLDictionary, NULL);
1208
1209 if (source == dest) {
1210 WMPropList *keys = WMGetPLDictionaryKeys(dest);
1211 int i;
1212
1213 for (i = 0; i < WMGetArrayItemCount(keys->d.array); i++) {
1214 WMRemoveFromPLDictionary(dest, WMGetFromArray(keys->d.array, i));
1215 }
1216 WMReleasePropList(keys);
1217 return dest;
1218 }
1219
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);
1229 }
1230 }
1231
1232 return dest;
1233 }
1234
WMGetPropListItemCount(WMPropList * plist)1235 int WMGetPropListItemCount(WMPropList * plist)
1236 {
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;
1249 }
1250
1251 return 0;
1252 }
1253
WMIsPLString(WMPropList * plist)1254 Bool WMIsPLString(WMPropList * plist)
1255 {
1256 if (plist)
1257 return (plist->type == WPLString);
1258 else
1259 return False;
1260 }
1261
WMIsPLData(WMPropList * plist)1262 Bool WMIsPLData(WMPropList * plist)
1263 {
1264 if (plist)
1265 return (plist->type == WPLData);
1266 else
1267 return False;
1268 }
1269
WMIsPLArray(WMPropList * plist)1270 Bool WMIsPLArray(WMPropList * plist)
1271 {
1272 if (plist)
1273 return (plist->type == WPLArray);
1274 else
1275 return False;
1276 }
1277
WMIsPLDictionary(WMPropList * plist)1278 Bool WMIsPLDictionary(WMPropList * plist)
1279 {
1280 if (plist)
1281 return (plist->type == WPLDictionary);
1282 else
1283 return False;
1284 }
1285
WMIsPropListEqualTo(WMPropList * plist,WMPropList * other)1286 Bool WMIsPropListEqualTo(WMPropList * plist, WMPropList * other)
1287 {
1288 WMPropList *key1, *item1, *item2;
1289 WMHashEnumerator enumerator;
1290 int n, i;
1291
1292 if (plist->type != other->type)
1293 return False;
1294
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);
1301 }
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;
1313 }
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;
1323 }
1324 return True;
1325 default:
1326 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1327 wassertrv(False, False);
1328 break;
1329 }
1330
1331 return False;
1332 }
1333
WMGetFromPLString(WMPropList * plist)1334 char *WMGetFromPLString(WMPropList * plist)
1335 {
1336 wassertrv(plist->type == WPLString, NULL);
1337
1338 return plist->d.string;
1339 }
1340
WMGetFromPLData(WMPropList * plist)1341 WMData *WMGetFromPLData(WMPropList * plist)
1342 {
1343 wassertrv(plist->type == WPLData, NULL);
1344
1345 return plist->d.data;
1346 }
1347
WMGetPLDataBytes(WMPropList * plist)1348 const unsigned char *WMGetPLDataBytes(WMPropList * plist)
1349 {
1350 wassertrv(plist->type == WPLData, NULL);
1351
1352 return WMDataBytes(plist->d.data);
1353 }
1354
WMGetPLDataLength(WMPropList * plist)1355 int WMGetPLDataLength(WMPropList * plist)
1356 {
1357 wassertrv(plist->type == WPLData, 0);
1358
1359 return WMGetDataLength(plist->d.data);
1360 }
1361
WMGetFromPLArray(WMPropList * plist,int index)1362 WMPropList *WMGetFromPLArray(WMPropList * plist, int index)
1363 {
1364 wassertrv(plist->type == WPLArray, NULL);
1365
1366 return WMGetFromArray(plist->d.array, index);
1367 }
1368
WMGetFromPLDictionary(WMPropList * plist,WMPropList * key)1369 WMPropList *WMGetFromPLDictionary(WMPropList * plist, WMPropList * key)
1370 {
1371 wassertrv(plist->type == WPLDictionary, NULL);
1372
1373 return WMHashGet(plist->d.dict, key);
1374 }
1375
WMGetPLDictionaryKeys(WMPropList * plist)1376 WMPropList *WMGetPLDictionaryKeys(WMPropList * plist)
1377 {
1378 WMPropList *array, *key;
1379 WMHashEnumerator enumerator;
1380
1381 wassertrv(plist->type == WPLDictionary, NULL);
1382
1383 array = (WMPropList *) wmalloc(sizeof(W_PropList));
1384 array->type = WPLArray;
1385 array->d.array = WMCreateArray(WMCountHashTable(plist->d.dict));
1386 array->retainCount = 1;
1387
1388 enumerator = WMEnumerateHashTable(plist->d.dict);
1389 while ((key = WMNextHashEnumeratorKey(&enumerator))) {
1390 WMAddToArray(array->d.array, WMRetainPropList(key));
1391 }
1392
1393 return array;
1394 }
1395
WMShallowCopyPropList(WMPropList * plist)1396 WMPropList *WMShallowCopyPropList(WMPropList * plist)
1397 {
1398 WMPropList *ret = NULL;
1399 WMPropList *key, *item;
1400 WMHashEnumerator e;
1401 WMData *data;
1402 int i;
1403
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;
1418
1419 for (i = 0; i < WMGetArrayItemCount(ret->d.array); i++)
1420 WMRetainPropList(WMGetFromArray(ret->d.array, i));
1421
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);
1428 }
1429 break;
1430 default:
1431 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1432 wassertrv(False, NULL);
1433 break;
1434 }
1435
1436 return ret;
1437 }
1438
WMDeepCopyPropList(WMPropList * plist)1439 WMPropList *WMDeepCopyPropList(WMPropList * plist)
1440 {
1441 WMPropList *ret = NULL;
1442 WMPropList *key, *item;
1443 WMHashEnumerator e;
1444 WMData *data;
1445 int i;
1446
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);
1461 }
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.
1469 */
1470 while (WMNextHashEnumeratorItemAndKey(&e, (void **)&item, (void **)&key)) {
1471 WMHashInsert(ret->d.dict, WMDeepCopyPropList(key), WMDeepCopyPropList(item));
1472 }
1473 break;
1474 default:
1475 wwarning(_("Used proplist functions on non-WMPropLists objects"));
1476 wassertrv(False, NULL);
1477 break;
1478 }
1479
1480 return ret;
1481 }
1482
WMCreatePropListFromDescription(const char * desc)1483 WMPropList *WMCreatePropListFromDescription(const char *desc)
1484 {
1485 WMPropList *plist = NULL;
1486 PLData *pldata;
1487
1488 pldata = (PLData *) wmalloc(sizeof(PLData));
1489 pldata->ptr = desc;
1490 pldata->lineNumber = 1;
1491
1492 plist = getPropList(pldata);
1493
1494 if (getNonSpaceChar(pldata) != 0 && plist) {
1495 COMPLAIN(pldata, _("extra data after end of property list"));
1496 /*
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)
1501 */
1502 WMReleasePropList(plist);
1503 plist = NULL;
1504 }
1505
1506 wfree(pldata);
1507
1508 return plist;
1509 }
1510
WMGetPropListDescription(WMPropList * plist,Bool indented)1511 char *WMGetPropListDescription(WMPropList * plist, Bool indented)
1512 {
1513 return (indented ? indentedDescription(plist, 0) : description(plist));
1514 }
1515
WMReadPropListFromFile(const char * file)1516 WMPropList *WMReadPropListFromFile(const char *file)
1517 {
1518 WMPropList *plist = NULL;
1519 PLData *pldata;
1520 char *read_buf;
1521 FILE *f;
1522 struct stat stbuf;
1523 size_t length;
1524
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;
1530 }
1531
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;
1538 }
1539
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);
1544 }
1545 fclose(f);
1546 wfree(read_buf);
1547 return NULL;
1548 }
1549 read_buf[length] = '\0';
1550 fclose(f);
1551
1552 pldata = (PLData *) wmalloc(sizeof(PLData));
1553 pldata->ptr = read_buf;
1554 pldata->filename = file;
1555 pldata->lineNumber = 1;
1556
1557 plist = getPropList(pldata);
1558
1559 if (getNonSpaceChar(pldata) != 0 && plist) {
1560 COMPLAIN(pldata, _("extra data after end of property list"));
1561 /*
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)
1566 */
1567 WMReleasePropList(plist);
1568 plist = NULL;
1569 }
1570
1571 wfree(read_buf);
1572 wfree(pldata);
1573
1574 return plist;
1575 }
1576
WMReadPropListFromPipe(const char * command)1577 WMPropList *WMReadPropListFromPipe(const char *command)
1578 {
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;
1586
1587 file = popen(command, "r");
1588
1589 if (!file) {
1590 werror(_("%s:could not open menu file"), command);
1591 return NULL;
1592 }
1593
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);
1600
1601 remain_size -= line_size;
1602 read_ptr += line_size;
1603
1604 if (remain_size < block_read_margin) {
1605 size_t read_length;
1606
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;
1611 }
1612 }
1613
1614 pclose(file);
1615
1616 pldata = (PLData *) wmalloc(sizeof(PLData));
1617 pldata->ptr = read_buf;
1618 pldata->filename = command;
1619 pldata->lineNumber = 1;
1620
1621 plist = getPropList(pldata);
1622
1623 if (getNonSpaceChar(pldata) != 0 && plist) {
1624 COMPLAIN(pldata, _("extra data after end of property list"));
1625 /*
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)
1630 */
1631 WMReleasePropList(plist);
1632 plist = NULL;
1633 }
1634
1635 wfree(read_buf);
1636 wfree(pldata);
1637
1638 return plist;
1639 }
1640
1641 /* TODO: review this function's code */
1642
WMWritePropListToFile(WMPropList * plist,const char * path)1643 Bool WMWritePropListToFile(WMPropList * plist, const char *path)
1644 {
1645 char *thePath = NULL;
1646 char *desc;
1647 FILE *theFile;
1648 #ifdef HAVE_MKSTEMP
1649 int fd, mask;
1650 #endif
1651
1652 if (!wmkdirhier(path))
1653 return False;
1654
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");
1659
1660 #ifdef HAVE_MKSTEMP
1661 /*
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.
1669 */
1670 mask = umask(S_IRWXG | S_IRWXO);
1671 if ((fd = mkstemp(thePath)) < 0) {
1672 werror(_("mkstemp (%s) failed"), thePath);
1673 goto failure;
1674 }
1675 umask(mask);
1676 fchmod(fd, 0666 & ~mask);
1677 if ((theFile = fdopen(fd, "wb")) == NULL) {
1678 close(fd);
1679 }
1680 #else
1681 if (mktemp(thePath) == NULL) {
1682 werror(_("mktemp (%s) failed"), thePath);
1683 goto failure;
1684 }
1685 theFile = fopen(thePath, "wb");
1686 #endif
1687
1688 if (theFile == NULL) {
1689 werror(_("open (%s) failed"), thePath);
1690 goto failure;
1691 }
1692
1693 desc = indentedDescription(plist, 0);
1694
1695 if (fprintf(theFile, "%s\n", desc) != strlen(desc) + 1) {
1696 werror(_("writing to file: %s failed"), thePath);
1697 wfree(desc);
1698 goto failure;
1699 }
1700
1701 wfree(desc);
1702
1703 (void)fsync(fileno(theFile));
1704 if (fclose(theFile) != 0) {
1705 werror(_("fclose (%s) failed"), thePath);
1706 goto failure;
1707 }
1708
1709 /* If we used a temporary file, we still need to rename() it be the
1710 * real file. Also, we need to try to retain the file attributes of
1711 * the original file we are overwriting (if we are) */
1712 if (rename(thePath, path) != 0) {
1713 werror(_("rename ('%s' to '%s') failed"), thePath, path);
1714 goto failure;
1715 }
1716
1717 wfree(thePath);
1718 return True;
1719
1720 failure:
1721 unlink(thePath);
1722 wfree(thePath);
1723 return False;
1724 }
1725
1726 /*
1727 * create a directory hierarchy
1728 *
1729 * if the last octet of `path' is `/', the full path is
1730 * assumed to be a directory; otherwise path is assumed to be a
1731 * file, and the last component is stripped off. the rest is the
1732 * the hierarchy to be created.
1733 *
1734 * refuses to create anything outside $WMAKER_USER_ROOT
1735 *
1736 * returns 1 on success, 0 on failure
1737 */
wmkdirhier(const char * path)1738 int wmkdirhier(const char *path)
1739 {
1740 const char *t;
1741 char *thePath = NULL, buf[1024];
1742 size_t p, plen;
1743 struct stat st;
1744
1745 /* Only create directories under $WMAKER_USER_ROOT */
1746 if ((t = wusergnusteppath()) == NULL)
1747 return 0;
1748 if (strncmp(path, t, strlen(t)) != 0)
1749 return 0;
1750
1751 thePath = wstrdup(path);
1752 /* Strip the trailing component if it is a file */
1753 p = strlen(thePath);
1754 while (p && thePath[p] != '/')
1755 thePath[p--] = '\0';
1756
1757 thePath[p] = '\0';
1758
1759 /* Shortcut if it already exists */
1760 if (stat(thePath, &st) == 0) {
1761 wfree(thePath);
1762 if (S_ISDIR(st.st_mode)) {
1763 /* Is a directory alright */
1764 return 1;
1765 } else {
1766 /* Exists, but not a directory, the caller
1767 * might just as well abort now */
1768 return 0;
1769 }
1770 }
1771
1772 memset(buf, 0, sizeof(buf));
1773 strncpy(buf, t, sizeof(buf) - 1);
1774 p = strlen(buf);
1775 plen = strlen(thePath);
1776
1777 do {
1778 while (p++ < plen && thePath[p] != '/')
1779 ;
1780
1781 strncpy(buf, thePath, p);
1782 if (mkdir(buf, 0777) == -1 && errno == EEXIST &&
1783 stat(buf, &st) == 0 && !S_ISDIR(st.st_mode)) {
1784 werror(_("Could not create component %s"), buf);
1785 wfree(thePath);
1786 return 0;
1787 }
1788 } while (p < plen);
1789
1790 wfree(thePath);
1791 return 1;
1792 }
1793
1794 /* ARGSUSED2 */
wrmdirhier_fn(const char * path,const struct stat * st,int type,struct FTW * ftw)1795 static int wrmdirhier_fn(const char *path, const struct stat *st,
1796 int type, struct FTW *ftw)
1797 {
1798 /* Parameter not used, but tell the compiler that it is ok */
1799 (void) st;
1800 (void) ftw;
1801
1802 switch(type) {
1803 case FTW_D:
1804 break;
1805 case FTW_DP:
1806 return rmdir(path);
1807 break;
1808 case FTW_F:
1809 case FTW_SL:
1810 case FTW_SLN:
1811 return unlink(path);
1812 break;
1813 case FTW_DNR:
1814 case FTW_NS:
1815 default:
1816 return EPERM;
1817 }
1818
1819 /* NOTREACHED */
1820 return 0;
1821 }
1822
1823 /*
1824 * remove a directory hierarchy
1825 *
1826 * refuses to remove anything outside $WMAKER_USER_ROOT
1827 *
1828 * returns 1 on success, 0 on failure
1829 *
1830 * TODO: revisit what's error and what's not
1831 *
1832 * with inspirations from OpenBSD's bin/rm/rm.c
1833 */
wrmdirhier(const char * path)1834 int wrmdirhier(const char *path)
1835 {
1836 struct stat st;
1837 int error;
1838 const char *t;
1839
1840 /* Only remove directories under $WMAKER_USER_ROOT */
1841 if ((t = wusergnusteppath()) == NULL)
1842 return EPERM;
1843 if (strncmp(path, t, strlen(t)) != 0)
1844 return EPERM;
1845
1846 /* Shortcut if it doesn't exist to begin with */
1847 if (stat(path, &st) == -1)
1848 return ENOENT;
1849
1850 error = nftw(path, wrmdirhier_fn, 1, FTW_PHYS);
1851
1852 return error;
1853 }
1854