1 /*
2 qfplist.c
3
4 Property list management
5
6 Copyright (C) 2000 Jeff Teunissen <deek@d2dc.net>
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to:
21
22 Free Software Foundation, Inc.
23 59 Temple Place - Suite 330
24 Boston, MA 02111-1307, USA
25
26 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #if defined(_WIN32) && defined(HAVE_MALLOC_H)
36 #include <malloc.h>
37 #endif
38
39 #include "qfalloca.h"
40
41 #include "QF/dstring.h"
42 #include "QF/hash.h"
43 #include "QF/qfplist.h"
44 #include "QF/qtypes.h"
45 #include "QF/sys.h"
46
47 /*
48 Generic property list item.
49 */
50 struct plitem_s {
51 pltype_t type;
52 void *data;
53 };
54
55 /*
56 Dictionaries
57 */
58 struct dictkey_s {
59 char *key;
60 plitem_t *value;
61 };
62 typedef struct dictkey_s dictkey_t;
63
64 /*
65 Arrays
66 */
67 struct plarray_s {
68 int numvals; ///< Number of items in array
69 int maxvals; ///< Number of items that can be stored
70 ///< before a realloc is necesary.
71 struct plitem_s **values; ///< Array data
72 };
73 typedef struct plarray_s plarray_t;
74
75 /*
76 Typeless, unformatted binary data
77 */
78 struct plbinary_s {
79 size_t size;
80 void *data;
81 };
82 typedef struct plbinary_s plbinary_t;
83
84 typedef struct pldata_s { // Unparsed property list string
85 const char *ptr;
86 unsigned int end;
87 unsigned int pos;
88 unsigned int line;
89 const char *error;
90 } pldata_t;
91
92 // Ugly defines for fast checking and conversion from char to number
93 #define inrange(ch,min,max) ((ch) >= (min) && (ch) <= (max))
94 #define char2num(ch) \
95 (inrange((ch), '0', '9') ? ((ch) - '0') \
96 : 10 + (inrange((ch), 'a', 'f') ? ((ch) - 'a') \
97 : ((ch) - 'A')))
98
99 static byte quotable_bitmap[32];
100 static inline int
is_quotable(byte x)101 is_quotable (byte x)
102 {
103 return quotable_bitmap[x / 8] & (1 << (x % 8));
104 }
105
106 static void
init_quotables(void)107 init_quotables (void)
108 {
109 const char *unquotables = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
110 "abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^";
111 const byte *c;
112 memset (quotable_bitmap, ~0, sizeof (quotable_bitmap));
113 for (c = (byte *) unquotables; *c; c++)
114 quotable_bitmap[*c / 8] &= ~(1 << (*c % 8));
115 }
116
117 static plitem_t *PL_ParsePropertyListItem (pldata_t *);
118 static qboolean PL_SkipSpace (pldata_t *);
119 static char *PL_ParseQuotedString (pldata_t *);
120 static char *PL_ParseUnquotedString (pldata_t *);
121 static char *PL_ParseData (pldata_t *, int *);
122
123 static const char *
dict_get_key(const void * i,void * unused)124 dict_get_key (const void *i, void *unused)
125 {
126 dictkey_t *item = (dictkey_t *) i;
127 return item->key;
128 }
129
130 static void
dict_free(void * i,void * unused)131 dict_free (void *i, void *unused)
132 {
133 dictkey_t *item = (dictkey_t *) i;
134 free (item->key);
135 if (item->value) // Make descended stuff get freed
136 PL_Free (item->value);
137 free (item);
138 }
139
140 static plitem_t *
PL_NewItem(pltype_t type)141 PL_NewItem (pltype_t type)
142 {
143 plitem_t *item = calloc (1, sizeof (plitem_t));
144 item->type = type;
145 return item;
146 }
147
148 VISIBLE plitem_t *
PL_NewDictionary(void)149 PL_NewDictionary (void)
150 {
151 plitem_t *item = PL_NewItem (QFDictionary);
152 hashtab_t *dict = Hash_NewTable (1021, dict_get_key, dict_free, NULL);
153 item->data = dict;
154 return item;
155 }
156
157 VISIBLE plitem_t *
PL_NewArray(void)158 PL_NewArray (void)
159 {
160 plitem_t *item = PL_NewItem (QFArray);
161 plarray_t *array = calloc (1, sizeof (plarray_t));
162 item->data = array;
163 return item;
164 }
165
166 VISIBLE plitem_t *
PL_NewData(void * data,size_t size)167 PL_NewData (void *data, size_t size)
168 {
169 plitem_t *item = PL_NewItem (QFBinary);
170 plbinary_t *bin = malloc (sizeof (plbinary_t));
171 item->data = bin;
172 bin->data = data;
173 bin->size = size;
174 return item;
175 }
176
177 static plitem_t *
new_string(char * str)178 new_string (char *str)
179 {
180 plitem_t *item = PL_NewItem (QFString);
181 item->data = str;
182 return item;
183 }
184
185 VISIBLE plitem_t *
PL_NewString(const char * str)186 PL_NewString (const char *str)
187 {
188 return new_string (strdup (str));
189 }
190
191 VISIBLE void
PL_Free(plitem_t * item)192 PL_Free (plitem_t *item)
193 {
194 switch (item->type) {
195 case QFDictionary:
196 Hash_DelTable (item->data);
197 break;
198
199 case QFArray: {
200 int i = ((plarray_t *) item->data)->numvals;
201
202 while (i-- > 0) {
203 PL_Free (((plarray_t *) item->data)->values[i]);
204 }
205 free (((plarray_t *) item->data)->values);
206 free (item->data);
207 }
208 break;
209
210 case QFBinary:
211 free (((plbinary_t *) item->data)->data);
212 free (item->data);
213 break;
214
215 case QFString:
216 free (item->data);
217 break;
218 }
219 free (item);
220 }
221
222 VISIBLE const char *
PL_String(plitem_t * string)223 PL_String (plitem_t *string)
224 {
225 if (string->type != QFString)
226 return NULL;
227 return string->data;
228 }
229
230 VISIBLE plitem_t *
PL_ObjectForKey(plitem_t * dict,const char * key)231 PL_ObjectForKey (plitem_t *dict, const char *key)
232 {
233 hashtab_t *table = (hashtab_t *) dict->data;
234 dictkey_t *k;
235
236 if (dict->type != QFDictionary)
237 return NULL;
238
239 k = (dictkey_t *) Hash_Find (table, key);
240 return k ? k->value : NULL;
241 }
242
243 VISIBLE plitem_t *
PL_RemoveObjectForKey(plitem_t * dict,const char * key)244 PL_RemoveObjectForKey (plitem_t *dict, const char *key)
245 {
246 hashtab_t *table = (hashtab_t *) dict->data;
247 dictkey_t *k;
248 plitem_t *value;
249
250 if (dict->type != QFDictionary)
251 return NULL;
252
253 k = (dictkey_t *) Hash_Del (table, key);
254 if (!k)
255 return NULL;
256 value = k->value;
257 k->value = 0;
258 dict_free (k, 0);
259 return value;
260 }
261
262 VISIBLE plitem_t *
PL_D_AllKeys(plitem_t * dict)263 PL_D_AllKeys (plitem_t *dict)
264 {
265 void **list, **l;
266 dictkey_t *current;
267 plitem_t *array;
268
269 if (dict->type != QFDictionary)
270 return NULL;
271
272 if (!(l = list = Hash_GetList ((hashtab_t *) dict->data)))
273 return NULL;
274
275 if (!(array = PL_NewArray ()))
276 return NULL;
277
278 while ((current = (dictkey_t *) *l++)) {
279 PL_A_AddObject (array, PL_NewString (current->key));
280 }
281 free (list);
282
283 return array;
284 }
285
286 VISIBLE int
PL_D_NumKeys(plitem_t * dict)287 PL_D_NumKeys (plitem_t *dict)
288 {
289 if (dict->type != QFDictionary)
290 return 0;
291 return Hash_NumElements ((hashtab_t *) dict->data);
292 }
293
294 VISIBLE plitem_t *
PL_ObjectAtIndex(plitem_t * array,int index)295 PL_ObjectAtIndex (plitem_t *array, int index)
296 {
297 plarray_t *arr = (plarray_t *) array->data;
298
299 if (array->type != QFArray)
300 return NULL;
301
302 return index >= 0 && index < arr->numvals ? arr->values[index] : NULL;
303 }
304
305 VISIBLE qboolean
PL_D_AddObject(plitem_t * dict,const char * key,plitem_t * value)306 PL_D_AddObject (plitem_t *dict, const char *key, plitem_t *value)
307 {
308 dictkey_t *k;
309
310 if (dict->type != QFDictionary)
311 return false;
312
313 if ((k = Hash_Find ((hashtab_t *)dict->data, key))) {
314 PL_Free ((plitem_t *) k->value);
315 k->value = value;
316 } else {
317 k = malloc (sizeof (dictkey_t));
318
319 if (!k)
320 return false;
321
322 k->key = strdup (key);
323 k->value = value;
324
325 Hash_Add ((hashtab_t *)dict->data, k);
326 }
327 return true;
328 }
329
330 VISIBLE qboolean
PL_A_InsertObjectAtIndex(plitem_t * array,plitem_t * item,int index)331 PL_A_InsertObjectAtIndex (plitem_t *array, plitem_t *item, int index)
332 {
333 plarray_t *arr;
334
335 if (array->type != QFArray)
336 return false;
337
338 arr = (plarray_t *)array->data;
339
340 if (arr->numvals == arr->maxvals) {
341 int size = (arr->maxvals + 128) * sizeof (plitem_t *);
342 plitem_t **tmp = realloc (arr->values, size);
343
344 if (!tmp)
345 return false;
346
347 arr->maxvals += 128;
348 arr->values = tmp;
349 memset (arr->values + arr->numvals, 0,
350 (arr->maxvals - arr->numvals) * sizeof (plitem_t *));
351 }
352
353 if (index == -1)
354 index = arr->numvals;
355
356 if (index < 0 || index > arr->numvals)
357 return false;
358
359 memmove (arr->values + index + 1, arr->values + index,
360 (arr->numvals - index) * sizeof (plitem_t *));
361 arr->values[index] = item;
362 arr->numvals++;
363 return true;
364 }
365
366 VISIBLE qboolean
PL_A_AddObject(plitem_t * array,plitem_t * item)367 PL_A_AddObject (plitem_t *array, plitem_t *item)
368 {
369 return PL_A_InsertObjectAtIndex (array, item, -1);
370 }
371
372 VISIBLE int
PL_A_NumObjects(plitem_t * array)373 PL_A_NumObjects (plitem_t *array)
374 {
375 if (array->type != QFArray)
376 return 0;
377 return ((plarray_t *) array->data)->numvals;
378 }
379
380 VISIBLE plitem_t *
PL_RemoveObjectAtIndex(plitem_t * array,int index)381 PL_RemoveObjectAtIndex (plitem_t *array, int index)
382 {
383 plarray_t *arr;
384 plitem_t *item;
385
386 if (array->type != QFArray)
387 return 0;
388
389 arr = (plarray_t *)array->data;
390
391 if (index < 0 || index >= arr->numvals)
392 return 0;
393
394 item = arr->values[index];
395 arr->numvals--;
396 while (index < arr->numvals) {
397 arr->values[index] = arr->values[index + 1];
398 index++;
399 }
400
401 return item;
402 }
403
404 static qboolean
PL_SkipSpace(pldata_t * pl)405 PL_SkipSpace (pldata_t *pl)
406 {
407 while (pl->pos < pl->end) {
408 char c = pl->ptr[pl->pos];
409
410 if (!isspace ((byte) c)) {
411 if (c == '/' && pl->pos < pl->end - 1) { // check for comments
412 if (pl->ptr[pl->pos + 1] == '/') {
413 pl->pos += 2;
414
415 while (pl->pos < pl->end) {
416 c = pl->ptr[pl->pos];
417
418 if (c == '\n')
419 break;
420 pl->pos++;
421 }
422 if (pl->pos >= pl->end) {
423 pl->error = "Reached end of string in comment";
424 return false;
425 }
426 } else if (pl->ptr[pl->pos + 1] == '*') { // "/*" comments
427 pl->pos += 2;
428
429 while (pl->pos < pl->end) {
430 c = pl->ptr[pl->pos];
431
432 if (c == '\n') {
433 pl->line++;
434 } else if (c == '*' && pl->pos < pl->end - 1
435 && pl->ptr[pl->pos+1] == '/') {
436 pl->pos++;
437 break;
438 }
439 pl->pos++;
440 }
441 if (pl->pos >= pl->end) {
442 pl->error = "Reached end of string in comment";
443 return false;
444 }
445 } else {
446 return true;
447 }
448 } else {
449 return true;
450 }
451 }
452 if (c == '\n') {
453 pl->line++;
454 }
455 pl->pos++;
456 }
457 pl->error = "Reached end of string";
458 return false;
459 }
460
461 static inline byte
from_hex(byte a)462 from_hex (byte a)
463 {
464 if (a >= '0' && a <= '9')
465 return a - '0';
466 if (a >= 'A' && a <= 'F')
467 return a - 'A' + 10;
468 return a - 'a' + 10;
469 }
470
471 static inline byte
make_byte(byte h,byte l)472 make_byte (byte h, byte l)
473 {
474 return (from_hex (h) << 4) | from_hex (l);
475 }
476
477 static char *
PL_ParseData(pldata_t * pl,int * len)478 PL_ParseData (pldata_t *pl, int *len)
479 {
480 unsigned start = ++pl->pos;
481 int nibbles = 0, i;
482 char *str, c;
483
484 while (pl->pos < pl->end) {
485 c = pl->ptr[pl->pos++];
486 if (isxdigit ((byte) c)) {
487 nibbles++;
488 continue;
489 }
490 if (c == '>') {
491 if (nibbles & 1) {
492 pl->error = "invalid data, missing nibble";
493 return NULL;
494 }
495 *len = nibbles / 2;
496 str = malloc (*len);
497 for (i = 0; i < *len; i++)
498 str[i] = make_byte (pl->ptr[start + i * 2],
499 pl->ptr[start + i * 2 + 1]);
500 return str;
501 }
502 pl->error = "invalid character in data";
503 return NULL;
504 }
505 pl->error = "Reached end of string while parsing data";
506 return NULL;
507 }
508
509 static char *
PL_ParseQuotedString(pldata_t * pl)510 PL_ParseQuotedString (pldata_t *pl)
511 {
512 unsigned int start = ++pl->pos;
513 unsigned int escaped = 0;
514 unsigned int shrink = 0;
515 qboolean hex = false;
516 qboolean long_string = false;
517 char *str;
518
519 if (pl->ptr[pl->pos] == '"' &&
520 pl->ptr[pl->pos + 1] == '"') {
521 long_string = true;
522 start += 2;
523 pl->pos += 2;
524 }
525
526 while (pl->pos < pl->end) {
527
528 char c = pl->ptr[pl->pos];
529
530 if (escaped) {
531 if (escaped == 1 && inrange (c, '0', '7')) {
532 escaped++;
533 hex = false;
534 } else if (escaped > 1) {
535 if (escaped == 2 && c == 'x') {
536 hex = true;
537 shrink++;
538 escaped++;
539 } else if (hex && inrange (escaped, 3, 4)
540 && isxdigit ((byte) c)) {
541 shrink++;
542 escaped++;
543 } else if (inrange (escaped, 1, 3) && inrange (c, '0', '7')) {
544 shrink++;
545 escaped++;
546 } else {
547 pl->pos--;
548 escaped = 0;
549 }
550 } else {
551 escaped = 0;
552 }
553 } else {
554 if (c == '\\') {
555 escaped = 1;
556 shrink++;
557 } else if (c == '"'
558 && (!long_string || (pl->ptr[pl->pos + 1] == '"'
559 && pl->ptr[pl->pos + 2] == '"'))) {
560 break;
561 }
562 }
563
564 if (c == '\n') {
565 pl->line++;
566 }
567
568 pl->pos++;
569 }
570
571 if (pl->pos >= pl->end) {
572 pl->error = "Reached end of string while parsing quoted string";
573 return NULL;
574 }
575
576 if (pl->pos - start - shrink == 0) {
577 str = strdup ("");
578 } else {
579 char *chars = alloca(pl->pos - start - shrink);
580 unsigned int j;
581 unsigned int k;
582
583 escaped = 0;
584 hex = false;
585
586 for (j = start, k = 0; j < pl->pos; j++) {
587
588 char c = pl->ptr[j];
589
590 if (escaped) {
591 if (escaped == 1 && inrange (c, '0', '7')) {
592 chars[k] = c - '0';
593 hex = false;
594 escaped++;
595 } else if (escaped > 1) {
596 if (escaped == 2 && c == 'x') {
597 hex = true;
598 escaped++;
599 } else if (hex && inrange (escaped, 3, 4)
600 && isxdigit ((byte) c)) {
601 chars[k] <<= 4;
602 chars[k] |= char2num (c);
603 escaped++;
604 } else if (inrange (escaped, 1, 3)
605 && inrange (c, '0', '7')) {
606 chars[k] <<= 3;
607 chars[k] |= (c - '0');
608 escaped++;
609 } else {
610 escaped = 0;
611 j--;
612 k++;
613 }
614 } else {
615 escaped = 0;
616 switch (c) {
617 case 'a':
618 chars[k] = '\a';
619 break;
620 case 'b':
621 chars[k] = '\b';
622 break;
623 case 't':
624 chars[k] = '\t';
625 break;
626 case 'r':
627 chars[k] = '\r';
628 break;
629 case 'n':
630 chars[k] = '\n';
631 break;
632 case 'v':
633 chars[k] = '\v';
634 break;
635 case 'f':
636 chars[k] = '\f';
637 break;
638 default:
639 chars[k] = c;
640 break;
641 }
642 k++;
643 }
644 } else {
645 chars[k] = c;
646 if (c == '\\') {
647 escaped = 1;
648 } else {
649 k++;
650 }
651 }
652 }
653 str = strncat (calloc ((pl->pos - start - shrink) + 1, 1), chars,
654 pl->pos - start - shrink);
655 }
656 if (long_string)
657 pl->pos += 2;
658 pl->pos++;
659 return str;
660 }
661
662 static char *
PL_ParseUnquotedString(pldata_t * pl)663 PL_ParseUnquotedString (pldata_t *pl)
664 {
665 unsigned int start = pl->pos;
666 char *str;
667
668 while (pl->pos < pl->end) {
669 if (is_quotable (pl->ptr[pl->pos]))
670 break;
671 pl->pos++;
672 }
673 str = strncat (calloc ((pl->pos - start) + 1, 1), &pl->ptr[start],
674 pl->pos - start);
675 return str;
676 }
677
678 static plitem_t *
PL_ParsePropertyListItem(pldata_t * pl)679 PL_ParsePropertyListItem (pldata_t *pl)
680 {
681 plitem_t *item = NULL;
682
683 if (!PL_SkipSpace (pl))
684 return NULL;
685
686 switch (pl->ptr[pl->pos]) {
687 case '{':
688 {
689 item = PL_NewDictionary ();
690
691 pl->pos++;
692
693 while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != '}') {
694 plitem_t *key;
695 plitem_t *value;
696
697 if (!(key = PL_ParsePropertyListItem (pl))) {
698 PL_Free (item);
699 return NULL;
700 }
701
702 if (!(PL_SkipSpace (pl))) {
703 PL_Free (key);
704 PL_Free (item);
705 return NULL;
706 }
707
708 if (key->type != QFString) {
709 pl->error = "Key is not a string";
710 PL_Free (key);
711 PL_Free (item);
712 return NULL;
713 }
714
715 if (pl->ptr[pl->pos] != '=') {
716 pl->error = "Unexpected character (expected '=')";
717 PL_Free (key);
718 PL_Free (item);
719 return NULL;
720 }
721 pl->pos++;
722
723 // If there is no value, lose the key
724 if (!(value = PL_ParsePropertyListItem (pl))) {
725 PL_Free (key);
726 PL_Free (item);
727 return NULL;
728 }
729
730 if (!(PL_SkipSpace (pl))) {
731 PL_Free (key);
732 PL_Free (value);
733 PL_Free (item);
734 return NULL;
735 }
736
737 if (pl->ptr[pl->pos] == ';') {
738 pl->pos++;
739 } else if (pl->ptr[pl->pos] != '}') {
740 pl->error = "Unexpected character (wanted ';' or '}')";
741 PL_Free (key);
742 PL_Free (value);
743 PL_Free (item);
744 return NULL;
745 }
746
747 // Add the key/value pair to the dictionary
748 if (!PL_D_AddObject (item, PL_String (key), value)) {
749 PL_Free (key);
750 PL_Free (value);
751 PL_Free (item);
752 return NULL;
753 }
754 PL_Free (key);
755 }
756
757 if (pl->pos >= pl->end) { // Catch the error
758 pl->error = "Unexpected end of string when parsing dictionary";
759 PL_Free (item);
760 return NULL;
761 }
762 pl->pos++;
763
764 return item;
765 }
766
767 case '(': {
768 item = PL_NewArray ();
769
770 pl->pos++;
771
772 while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != ')') {
773 plitem_t *value;
774
775 if (!(value = PL_ParsePropertyListItem (pl))) {
776 PL_Free (item);
777 return NULL;
778 }
779
780 if (!(PL_SkipSpace (pl))) {
781 PL_Free (value);
782 PL_Free (item);
783 return NULL;
784 }
785
786 if (pl->ptr[pl->pos] == ',') {
787 pl->pos++;
788 } else if (pl->ptr[pl->pos] != ')') {
789 pl->error = "Unexpected character (wanted ',' or ')')";
790 PL_Free (value);
791 PL_Free (item);
792 return NULL;
793 }
794
795 if (!PL_A_AddObject (item, value)) {
796 pl->error = "Unexpected character (too many items in array)";
797 PL_Free (value);
798 PL_Free (item);
799 return NULL;
800 }
801 }
802 pl->pos++;
803
804 return item;
805 }
806
807 case '<': {
808 int len;
809 char *str = PL_ParseData (pl, &len);
810
811 if (!str) {
812 return NULL;
813 } else {
814 return PL_NewData (str, len);
815 }
816 }
817
818 case '"': {
819 char *str = PL_ParseQuotedString (pl);
820
821 if (!str) {
822 return NULL;
823 } else {
824 return new_string (str);
825 }
826 }
827
828 default: {
829 char *str = PL_ParseUnquotedString (pl);
830
831 if (!str) {
832 return NULL;
833 } else {
834 return new_string (str);
835 }
836 }
837 } // switch
838 }
839
840 VISIBLE plitem_t *
PL_GetPropertyList(const char * string)841 PL_GetPropertyList (const char *string)
842 {
843 pldata_t *pl = calloc (1, sizeof (pldata_t));
844 plitem_t *newpl = NULL;
845
846 if (!quotable_bitmap[0])
847 init_quotables ();
848
849 pl->ptr = string;
850 pl->pos = 0;
851 pl->end = strlen (string);
852 pl->error = NULL;
853 pl->line = 1;
854
855 if ((newpl = PL_ParsePropertyListItem (pl))) {
856 free (pl);
857 return newpl;
858 } else {
859 if (pl && pl->error && pl->error[0])
860 Sys_Printf ("plist: %d,%d: %s", pl->line, pl->pos, pl->error);
861 free (pl);
862 return NULL;
863 }
864 }
865
866 static void
write_tabs(dstring_t * dstr,int num)867 write_tabs (dstring_t *dstr, int num)
868 {
869 char *tabs = dstring_reservestr (dstr, num);
870
871 memset (tabs, '\t', num);
872 tabs[num] = 0;
873 }
874
875 static void
write_string_len(dstring_t * dstr,const char * str,int len)876 write_string_len (dstring_t *dstr, const char *str, int len)
877 {
878 char *dst = dstring_reservestr (dstr, len);
879 memcpy (dst, str, len);
880 dst[len] = 0;
881 }
882
883 static char
to_hex(byte b)884 to_hex (byte b)
885 {
886 char c = (b & 0xf) + '0';
887 if (c > '9')
888 c = c - '0' + 'A';
889 return c;
890 }
891
892 static void
write_binary(dstring_t * dstr,byte * binary,int len)893 write_binary (dstring_t *dstr, byte *binary, int len)
894 {
895 int i;
896 char *dst = dstring_reservestr (dstr, len * 2);
897 for (i = 0; i < len; i++) {
898 *dst++ = to_hex (binary[i] >> 4);
899 *dst++ = to_hex (binary[i]);
900 }
901 }
902
903 static void
write_string(dstring_t * dstr,const char * str)904 write_string (dstring_t *dstr, const char *str)
905 {
906 const char *s;
907 int len = 0;
908 char *dst;
909 int quoted = 0;
910
911 for (s = str; *s; s++) {
912 if (is_quotable (*s))
913 quoted = 1;
914 len++;
915 }
916 if (!quoted) {
917 dst = dstring_reservestr (dstr, len);
918 strcpy (dst, str);
919 return;
920 }
921 // assume worst case of all octal chars plus two quotes.
922 dst = dstring_reservestr (dstr, len * 4 + 2);
923 *dst++= '\"';
924 while (*str) {
925 if (*str && isascii ((byte) *str) && isprint ((byte) *str)
926 && *str != '\\' && *str != '\"') {
927 *dst++ = *str++;
928 continue;
929 }
930 if (*str) {
931 *dst++ = '\\';
932 switch (*str) {
933 case '\"':
934 case '\\':
935 *dst++ = *str;
936 break;
937 case '\n':
938 *dst++ = 'n';
939 break;
940 case '\a':
941 *dst++ = 'a';
942 break;
943 case '\b':
944 *dst++ = 'b';
945 break;
946 case '\f':
947 *dst++ = 'f';
948 break;
949 case '\r':
950 *dst++ = 'r';
951 break;
952 case '\t':
953 *dst++ = 't';
954 break;
955 case '\v':
956 *dst++ = 'v';
957 break;
958 default:
959 *dst++ = '0' + ((((byte) *str) >> 6) & 3);
960 *dst++ = '0' + ((((byte) *str) >> 3) & 7);
961 *dst++ = '0' + (((byte) *str) & 7);
962 break;
963 }
964 str++;
965 }
966 }
967 *dst++ = '\"';
968 *dst++ = 0;
969 dstr->size = dst - dstr->str;
970 }
971
972 static void
write_item(dstring_t * dstr,plitem_t * item,int level)973 write_item (dstring_t *dstr, plitem_t *item, int level)
974 {
975 void **list, **l;
976 dictkey_t *current;
977 plarray_t *array;
978 plbinary_t *binary;
979 int i;
980
981 switch (item->type) {
982 case QFDictionary:
983 write_string_len (dstr, "{\n", 2);
984 l = list = Hash_GetList ((hashtab_t *) item->data);
985 while ((current = (dictkey_t *) *l++)) {
986 write_tabs (dstr, level + 1);
987 write_string (dstr, current->key);
988 write_string_len (dstr, " = ", 3);
989 write_item (dstr, current->value, level + 1);
990 write_string_len (dstr, ";\n", 2);
991 }
992 free (list);
993 write_tabs (dstr, level);
994 write_string_len (dstr, "}", 1);
995 break;
996 case QFArray:
997 write_string_len (dstr, "(\n", 2);
998 array = (plarray_t *) item->data;
999 for (i = 0; i < array->numvals; i++) {
1000 write_tabs (dstr, level + 1);
1001 write_item (dstr, array->values[i], level + 1);
1002 if (i < array->numvals - 1)
1003 write_string_len (dstr, ",\n", 2);
1004 }
1005 write_string_len (dstr, "\n", 1);
1006 write_tabs (dstr, level);
1007 write_string_len (dstr, ")", 1);
1008 break;
1009 case QFBinary:
1010 write_string_len (dstr, "<", 1);
1011 binary = (plbinary_t *) item->data;
1012 write_binary (dstr, binary->data, binary->size);
1013 write_string_len (dstr, ">", 1);
1014 break;
1015 case QFString:
1016 write_string (dstr, item->data);
1017 break;
1018 default:
1019 break;
1020 }
1021 }
1022
1023 VISIBLE char *
PL_WritePropertyList(plitem_t * pl)1024 PL_WritePropertyList (plitem_t *pl)
1025 {
1026 dstring_t *dstr = dstring_newstr ();
1027
1028 if (!quotable_bitmap[0])
1029 init_quotables ();
1030 write_item (dstr, pl, 0);
1031 write_string_len (dstr, "\n", 1);
1032 return dstring_freeze (dstr);
1033 }
1034
1035 VISIBLE pltype_t
PL_Type(plitem_t * item)1036 PL_Type (plitem_t *item)
1037 {
1038 return item->type;
1039 }
1040