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