prop_dictionary.c revision 1.30 1 /* $NetBSD: prop_dictionary.c,v 1.30 2008/05/24 14:32:48 yamt Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <prop/prop_array.h>
33 #include <prop/prop_dictionary.h>
34 #include <prop/prop_string.h>
35 #include "prop_object_impl.h"
36 #include "prop_rb_impl.h"
37
38 #if !defined(_KERNEL) && !defined(_STANDALONE)
39 #include <errno.h>
40 #endif
41
42 /*
43 * We implement these like arrays, but we keep them sorted by key.
44 * This allows us to binary-search as well as keep externalized output
45 * sane-looking for human eyes.
46 */
47
48 #define EXPAND_STEP 16
49
50 /*
51 * prop_dictionary_keysym_t is allocated with space at the end to hold the
52 * key. This must be a regular object so that we can maintain sane iterator
53 * semantics -- we don't want to require that the caller release the result
54 * of prop_object_iterator_next().
55 *
56 * We'd like to have some small'ish keysym objects for up-to-16 characters
57 * in a key, some for up-to-32 characters in a key, and then a final bucket
58 * for up-to-128 characters in a key (not including NUL). Keys longer than
59 * 128 characters are not allowed.
60 */
61 struct _prop_dictionary_keysym {
62 struct _prop_object pdk_obj;
63 size_t pdk_size;
64 struct rb_node pdk_link;
65 char pdk_key[1];
66 /* actually variable length */
67 };
68
69 #define RBNODE_TO_PDK(n) \
70 ((struct _prop_dictionary_keysym *) \
71 ((uintptr_t)n - offsetof(struct _prop_dictionary_keysym, pdk_link)))
72
73 /* pdk_key[1] takes care of the NUL */
74 #define PDK_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16)
75 #define PDK_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32)
76 #define PDK_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128)
77
78 #define PDK_MAXKEY 128
79
80 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16")
81 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32")
82 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128")
83
84 struct _prop_dict_entry {
85 prop_dictionary_keysym_t pde_key;
86 prop_object_t pde_objref;
87 };
88
89 struct _prop_dictionary {
90 struct _prop_object pd_obj;
91 _PROP_RWLOCK_DECL(pd_rwlock)
92 struct _prop_dict_entry *pd_array;
93 unsigned int pd_capacity;
94 unsigned int pd_count;
95 int pd_flags;
96
97 uint32_t pd_version;
98 };
99
100 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */
101
102 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
103 "propdict")
104 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
105 "property dictionary container object")
106
107 static int _prop_dictionary_free(prop_stack_t, prop_object_t *);
108 static void _prop_dictionary_emergency_free(prop_object_t);
109 static bool _prop_dictionary_externalize(
110 struct _prop_object_externalize_context *,
111 void *);
112 static bool _prop_dictionary_equals(prop_object_t, prop_object_t,
113 void **, void **,
114 prop_object_t *, prop_object_t *);
115 static void _prop_dictionary_equals_finish(prop_object_t, prop_object_t);
116 static prop_object_iterator_t _prop_dictionary_iterator_locked(
117 prop_dictionary_t);
118 static prop_object_t _prop_dictionary_iterator_next_object_locked(void *);
119 static prop_object_t _prop_dictionary_get_keysym(prop_dictionary_t,
120 prop_dictionary_keysym_t,
121 bool);
122 static prop_object_t _prop_dictionary_get(prop_dictionary_t, const char *,
123 bool);
124
125 static const struct _prop_object_type _prop_object_type_dictionary = {
126 .pot_type = PROP_TYPE_DICTIONARY,
127 .pot_free = _prop_dictionary_free,
128 .pot_emergency_free = _prop_dictionary_emergency_free,
129 .pot_extern = _prop_dictionary_externalize,
130 .pot_equals = _prop_dictionary_equals,
131 .pot_equals_finish = _prop_dictionary_equals_finish,
132 };
133
134 static int _prop_dict_keysym_free(prop_stack_t, prop_object_t *);
135 static bool _prop_dict_keysym_externalize(
136 struct _prop_object_externalize_context *,
137 void *);
138 static bool _prop_dict_keysym_equals(prop_object_t, prop_object_t,
139 void **, void **,
140 prop_object_t *, prop_object_t *);
141
142 static const struct _prop_object_type _prop_object_type_dict_keysym = {
143 .pot_type = PROP_TYPE_DICT_KEYSYM,
144 .pot_free = _prop_dict_keysym_free,
145 .pot_extern = _prop_dict_keysym_externalize,
146 .pot_equals = _prop_dict_keysym_equals,
147 };
148
149 #define prop_object_is_dictionary(x) \
150 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary)
151 #define prop_object_is_dictionary_keysym(x) \
152 ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym)
153
154 #define prop_dictionary_is_immutable(x) \
155 (((x)->pd_flags & PD_F_IMMUTABLE) != 0)
156
157 struct _prop_dictionary_iterator {
158 struct _prop_object_iterator pdi_base;
159 unsigned int pdi_index;
160 };
161
162 /*
163 * Dictionary key symbols are immutable, and we are likely to have many
164 * duplicated key symbols. So, to save memory, we unique'ify key symbols
165 * so we only have to have one copy of each string.
166 */
167
168 static int
169 _prop_dict_keysym_rb_compare_nodes(const struct rb_node *n1,
170 const struct rb_node *n2)
171 {
172 const prop_dictionary_keysym_t pdk1 = RBNODE_TO_PDK(n1);
173 const prop_dictionary_keysym_t pdk2 = RBNODE_TO_PDK(n2);
174
175 return (strcmp(pdk1->pdk_key, pdk2->pdk_key));
176 }
177
178 static int
179 _prop_dict_keysym_rb_compare_key(const struct rb_node *n,
180 const void *v)
181 {
182 const prop_dictionary_keysym_t pdk = RBNODE_TO_PDK(n);
183 const char *cp = v;
184
185 return (strcmp(pdk->pdk_key, cp));
186 }
187
188 static const struct rb_tree_ops _prop_dict_keysym_rb_tree_ops = {
189 .rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes,
190 .rbto_compare_key = _prop_dict_keysym_rb_compare_key,
191 };
192
193 static struct rb_tree _prop_dict_keysym_tree;
194 static bool _prop_dict_keysym_tree_initialized;
195
196 _PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex)
197
198 static void
199 _prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
200 {
201
202 if (pdk->pdk_size <= PDK_SIZE_16)
203 _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk);
204 else if (pdk->pdk_size <= PDK_SIZE_32)
205 _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk);
206 else {
207 _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128);
208 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk);
209 }
210 }
211
212 /* ARGSUSED */
213 static int
214 _prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj)
215 {
216 prop_dictionary_keysym_t pdk = *obj;
217
218 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
219 _prop_rb_tree_remove_node(&_prop_dict_keysym_tree, &pdk->pdk_link);
220 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
221
222 _prop_dict_keysym_put(pdk);
223
224 return _PROP_OBJECT_FREE_DONE;
225 }
226
227 static bool
228 _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
229 void *v)
230 {
231 prop_dictionary_keysym_t pdk = v;
232
233 /* We externalize these as strings, and they're never empty. */
234
235 _PROP_ASSERT(pdk->pdk_key[0] != '\0');
236
237 if (_prop_object_externalize_start_tag(ctx, "string") == false ||
238 _prop_object_externalize_append_encoded_cstring(ctx,
239 pdk->pdk_key) == false ||
240 _prop_object_externalize_end_tag(ctx, "string") == false)
241 return (false);
242
243 return (true);
244 }
245
246 /* ARGSUSED */
247 static bool
248 _prop_dict_keysym_equals(prop_object_t v1, prop_object_t v2,
249 void **stored_pointer1, void **stored_pointer2,
250 prop_object_t *next_obj1, prop_object_t *next_obj2)
251 {
252 prop_dictionary_keysym_t pdk1 = v1;
253 prop_dictionary_keysym_t pdk2 = v2;
254
255 /*
256 * There is only ever one copy of a keysym at any given time,
257 * so we can reduce this to a simple pointer equality check.
258 */
259 if (pdk1 == pdk2)
260 return _PROP_OBJECT_EQUALS_TRUE;
261 else
262 return _PROP_OBJECT_EQUALS_FALSE;
263 }
264
265 static prop_dictionary_keysym_t
266 _prop_dict_keysym_alloc(const char *key)
267 {
268 prop_dictionary_keysym_t opdk, pdk;
269 const struct rb_node *n;
270 size_t size;
271
272 /*
273 * Check to see if this already exists in the tree. If it does,
274 * we just retain it and return it.
275 */
276 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
277 if (! _prop_dict_keysym_tree_initialized) {
278 _prop_rb_tree_init(&_prop_dict_keysym_tree,
279 &_prop_dict_keysym_rb_tree_ops);
280 _prop_dict_keysym_tree_initialized = true;
281 } else {
282 n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
283 if (n != NULL) {
284 opdk = RBNODE_TO_PDK(n);
285 prop_object_retain(opdk);
286 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
287 return (opdk);
288 }
289 }
290 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
291
292 /*
293 * Not in the tree. Create it now.
294 */
295
296 size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */;
297
298 if (size <= PDK_SIZE_16)
299 pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
300 else if (size <= PDK_SIZE_32)
301 pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
302 else if (size <= PDK_SIZE_128)
303 pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
304 else
305 pdk = NULL; /* key too long */
306
307 if (pdk == NULL)
308 return (NULL);
309
310 _prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym);
311
312 strcpy(pdk->pdk_key, key);
313 pdk->pdk_size = size;
314
315 /*
316 * We dropped the mutex when we allocated the new object, so
317 * we have to check again if it is in the tree.
318 */
319 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
320 n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
321 if (n != NULL) {
322 opdk = RBNODE_TO_PDK(n);
323 prop_object_retain(opdk);
324 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
325 _prop_dict_keysym_put(pdk);
326 return (opdk);
327 }
328 _prop_rb_tree_insert_node(&_prop_dict_keysym_tree, &pdk->pdk_link);
329 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
330 return (pdk);
331 }
332
333 int dont_free = 1;
334
335 static int
336 _prop_dictionary_free(prop_stack_t stack, prop_object_t *obj)
337 {
338 prop_dictionary_t pd = *obj;
339 prop_dictionary_keysym_t pdk;
340 prop_object_t po;
341
342 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
343 _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
344 (pd->pd_capacity != 0 && pd->pd_array != NULL));
345
346 /* The empty dictorinary is easy, handle that first. */
347 if (pd->pd_count == 0) {
348 if (pd->pd_array != NULL)
349 _PROP_FREE(pd->pd_array, M_PROP_DICT);
350
351 _PROP_RWLOCK_DESTROY(pd->pd_rwlock);
352
353 _PROP_POOL_PUT(_prop_dictionary_pool, pd);
354
355 return (_PROP_OBJECT_FREE_DONE);
356 }
357
358 po = pd->pd_array[pd->pd_count - 1].pde_objref;
359 _PROP_ASSERT(po != NULL);
360
361 if (stack == NULL) {
362 /*
363 * If we are in emergency release mode,
364 * just let caller recurse down.
365 */
366 *obj = po;
367 return (_PROP_OBJECT_FREE_FAILED);
368 }
369
370 /* Otherwise, try to push the current object on the stack. */
371 if (!_prop_stack_push(stack, pd, NULL, NULL, NULL)) {
372 /* Push failed, entering emergency release mode. */
373 return (_PROP_OBJECT_FREE_FAILED);
374 }
375 /* Object pushed on stack, caller will release it. */
376 --pd->pd_count;
377 pdk = pd->pd_array[pd->pd_count].pde_key;
378 _PROP_ASSERT(pdk != NULL);
379 prop_object_release(pdk);
380 *obj = po;
381 return (_PROP_OBJECT_FREE_RECURSE);
382 }
383
384 static void
385 _prop_dictionary_emergency_free(prop_object_t obj)
386 {
387 prop_dictionary_t pd = obj;
388 prop_dictionary_keysym_t pdk;
389
390 _PROP_ASSERT(pd->pd_count != 0);
391 --pd->pd_count;
392
393 pdk = pd->pd_array[pd->pd_count].pde_key;
394 _PROP_ASSERT(pdk != NULL);
395 prop_object_release(pdk);
396 }
397
398 static bool
399 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
400 void *v)
401 {
402 prop_dictionary_t pd = v;
403 prop_dictionary_keysym_t pdk;
404 struct _prop_object *po;
405 prop_object_iterator_t pi;
406 unsigned int i;
407 bool rv = false;
408
409 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
410
411 if (pd->pd_count == 0) {
412 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
413 return (_prop_object_externalize_empty_tag(ctx, "dict"));
414 }
415
416 if (_prop_object_externalize_start_tag(ctx, "dict") == false ||
417 _prop_object_externalize_append_char(ctx, '\n') == false)
418 goto out;
419
420 pi = _prop_dictionary_iterator_locked(pd);
421 if (pi == NULL)
422 goto out;
423
424 ctx->poec_depth++;
425 _PROP_ASSERT(ctx->poec_depth != 0);
426
427 while ((pdk = _prop_dictionary_iterator_next_object_locked(pi))
428 != NULL) {
429 po = _prop_dictionary_get_keysym(pd, pdk, true);
430 if (po == NULL ||
431 _prop_object_externalize_start_tag(ctx, "key") == false ||
432 _prop_object_externalize_append_encoded_cstring(ctx,
433 pdk->pdk_key) == false ||
434 _prop_object_externalize_end_tag(ctx, "key") == false ||
435 (*po->po_type->pot_extern)(ctx, po) == false) {
436 prop_object_iterator_release(pi);
437 goto out;
438 }
439 }
440
441 prop_object_iterator_release(pi);
442
443 ctx->poec_depth--;
444 for (i = 0; i < ctx->poec_depth; i++) {
445 if (_prop_object_externalize_append_char(ctx, '\t') == false)
446 goto out;
447 }
448 if (_prop_object_externalize_end_tag(ctx, "dict") == false)
449 goto out;
450
451 rv = true;
452
453 out:
454 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
455 return (rv);
456 }
457
458 /* ARGSUSED */
459 static bool
460 _prop_dictionary_equals(prop_object_t v1, prop_object_t v2,
461 void **stored_pointer1, void **stored_pointer2,
462 prop_object_t *next_obj1, prop_object_t *next_obj2)
463 {
464 prop_dictionary_t dict1 = v1;
465 prop_dictionary_t dict2 = v2;
466 uintptr_t idx;
467 bool rv = _PROP_OBJECT_EQUALS_FALSE;
468
469 if (dict1 == dict2)
470 return (_PROP_OBJECT_EQUALS_TRUE);
471
472 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
473
474 idx = (uintptr_t)*stored_pointer1;
475
476 if (idx == 0) {
477 if ((uintptr_t)dict1 < (uintptr_t)dict2) {
478 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
479 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
480 } else {
481 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
482 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
483 }
484 }
485
486 if (dict1->pd_count != dict2->pd_count)
487 goto out;
488
489 if (idx == dict1->pd_count) {
490 rv = _PROP_OBJECT_EQUALS_TRUE;
491 goto out;
492 }
493
494 _PROP_ASSERT(idx < dict1->pd_count);
495
496 *stored_pointer1 = (void *)(idx + 1);
497 *stored_pointer2 = (void *)(idx + 1);
498
499 *next_obj1 = &dict1->pd_array[idx].pde_objref;
500 *next_obj2 = &dict2->pd_array[idx].pde_objref;
501
502 if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key,
503 dict2->pd_array[idx].pde_key))
504 goto out;
505
506 return (_PROP_OBJECT_EQUALS_RECURSE);
507
508 out:
509 _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
510 _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
511 return (rv);
512 }
513
514 static void
515 _prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2)
516 {
517 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock);
518 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock);
519 }
520
521 static prop_dictionary_t
522 _prop_dictionary_alloc(unsigned int capacity)
523 {
524 prop_dictionary_t pd;
525 struct _prop_dict_entry *array;
526
527 if (capacity != 0) {
528 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
529 if (array == NULL)
530 return (NULL);
531 } else
532 array = NULL;
533
534 pd = _PROP_POOL_GET(_prop_dictionary_pool);
535 if (pd != NULL) {
536 _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
537
538 _PROP_RWLOCK_INIT(pd->pd_rwlock);
539 pd->pd_array = array;
540 pd->pd_capacity = capacity;
541 pd->pd_count = 0;
542 pd->pd_flags = 0;
543
544 pd->pd_version = 0;
545 } else if (array != NULL)
546 _PROP_FREE(array, M_PROP_DICT);
547
548 return (pd);
549 }
550
551 static bool
552 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
553 {
554 struct _prop_dict_entry *array, *oarray;
555
556 /*
557 * Dictionary must be WRITE-LOCKED.
558 */
559
560 oarray = pd->pd_array;
561
562 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
563 if (array == NULL)
564 return (false);
565 if (oarray != NULL)
566 memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
567 pd->pd_array = array;
568 pd->pd_capacity = capacity;
569
570 if (oarray != NULL)
571 _PROP_FREE(oarray, M_PROP_DICT);
572
573 return (true);
574 }
575
576 static prop_object_t
577 _prop_dictionary_iterator_next_object_locked(void *v)
578 {
579 struct _prop_dictionary_iterator *pdi = v;
580 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
581 prop_dictionary_keysym_t pdk = NULL;
582
583 _PROP_ASSERT(prop_object_is_dictionary(pd));
584
585 if (pd->pd_version != pdi->pdi_base.pi_version)
586 goto out; /* dictionary changed during iteration */
587
588 _PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
589
590 if (pdi->pdi_index == pd->pd_count)
591 goto out; /* we've iterated all objects */
592
593 pdk = pd->pd_array[pdi->pdi_index].pde_key;
594 pdi->pdi_index++;
595
596 out:
597 return (pdk);
598 }
599
600 static prop_object_t
601 _prop_dictionary_iterator_next_object(void *v)
602 {
603 struct _prop_dictionary_iterator *pdi = v;
604 prop_dictionary_t pd __unused = pdi->pdi_base.pi_obj;
605 prop_dictionary_keysym_t pdk;
606
607 _PROP_ASSERT(prop_object_is_dictionary(pd));
608
609 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
610 pdk = _prop_dictionary_iterator_next_object_locked(pdi);
611 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
612 return (pdk);
613 }
614
615 static void
616 _prop_dictionary_iterator_reset_locked(void *v)
617 {
618 struct _prop_dictionary_iterator *pdi = v;
619 prop_dictionary_t pd = pdi->pdi_base.pi_obj;
620
621 _PROP_ASSERT(prop_object_is_dictionary(pd));
622
623 pdi->pdi_index = 0;
624 pdi->pdi_base.pi_version = pd->pd_version;
625 }
626
627 static void
628 _prop_dictionary_iterator_reset(void *v)
629 {
630 struct _prop_dictionary_iterator *pdi = v;
631 prop_dictionary_t pd __unused = pdi->pdi_base.pi_obj;
632
633 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
634 _prop_dictionary_iterator_reset_locked(pdi);
635 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
636 }
637
638 /*
639 * prop_dictionary_create --
640 * Create a dictionary.
641 */
642 prop_dictionary_t
643 prop_dictionary_create(void)
644 {
645
646 return (_prop_dictionary_alloc(0));
647 }
648
649 /*
650 * prop_dictionary_create_with_capacity --
651 * Create a dictionary with the capacity to store N objects.
652 */
653 prop_dictionary_t
654 prop_dictionary_create_with_capacity(unsigned int capacity)
655 {
656
657 return (_prop_dictionary_alloc(capacity));
658 }
659
660 /*
661 * prop_dictionary_copy --
662 * Copy a dictionary. The new dictionary has an initial capacity equal
663 * to the number of objects stored int the original dictionary. The new
664 * dictionary contains refrences to the original dictionary's objects,
665 * not copies of those objects (i.e. a shallow copy).
666 */
667 prop_dictionary_t
668 prop_dictionary_copy(prop_dictionary_t opd)
669 {
670 prop_dictionary_t pd;
671 prop_dictionary_keysym_t pdk;
672 prop_object_t po;
673 unsigned int idx;
674
675 if (! prop_object_is_dictionary(opd))
676 return (NULL);
677
678 _PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
679
680 pd = _prop_dictionary_alloc(opd->pd_count);
681 if (pd != NULL) {
682 for (idx = 0; idx < opd->pd_count; idx++) {
683 pdk = opd->pd_array[idx].pde_key;
684 po = opd->pd_array[idx].pde_objref;
685
686 prop_object_retain(pdk);
687 prop_object_retain(po);
688
689 pd->pd_array[idx].pde_key = pdk;
690 pd->pd_array[idx].pde_objref = po;
691 }
692 pd->pd_count = opd->pd_count;
693 pd->pd_flags = opd->pd_flags;
694 }
695 _PROP_RWLOCK_UNLOCK(opd->pd_rwlock);
696 return (pd);
697 }
698
699 /*
700 * prop_dictionary_copy_mutable --
701 * Like prop_dictionary_copy(), but the resulting dictionary is
702 * mutable.
703 */
704 prop_dictionary_t
705 prop_dictionary_copy_mutable(prop_dictionary_t opd)
706 {
707 prop_dictionary_t pd;
708
709 if (! prop_object_is_dictionary(opd))
710 return (NULL);
711
712 pd = prop_dictionary_copy(opd);
713 if (pd != NULL)
714 pd->pd_flags &= ~PD_F_IMMUTABLE;
715
716 return (pd);
717 }
718
719 /*
720 * prop_dictionary_make_immutable --
721 * Set the immutable flag on that dictionary.
722 */
723 void
724 prop_dictionary_make_immutable(prop_dictionary_t pd)
725 {
726
727 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
728 if (prop_dictionary_is_immutable(pd) == false)
729 pd->pd_flags |= PD_F_IMMUTABLE;
730 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
731 }
732
733 /*
734 * prop_dictionary_count --
735 * Return the number of objects stored in the dictionary.
736 */
737 unsigned int
738 prop_dictionary_count(prop_dictionary_t pd)
739 {
740 unsigned int rv;
741
742 if (! prop_object_is_dictionary(pd))
743 return (0);
744
745 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
746 rv = pd->pd_count;
747 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
748
749 return (rv);
750 }
751
752 /*
753 * prop_dictionary_ensure_capacity --
754 * Ensure that the dictionary has the capacity to store the specified
755 * total number of objects (including the objects already stored in
756 * the dictionary).
757 */
758 bool
759 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
760 {
761 bool rv;
762
763 if (! prop_object_is_dictionary(pd))
764 return (false);
765
766 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
767 if (capacity > pd->pd_capacity)
768 rv = _prop_dictionary_expand(pd, capacity);
769 else
770 rv = true;
771 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
772 return (rv);
773 }
774
775 static prop_object_iterator_t
776 _prop_dictionary_iterator_locked(prop_dictionary_t pd)
777 {
778 struct _prop_dictionary_iterator *pdi;
779
780 if (! prop_object_is_dictionary(pd))
781 return (NULL);
782
783 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
784 if (pdi == NULL)
785 return (NULL);
786 pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
787 pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
788 prop_object_retain(pd);
789 pdi->pdi_base.pi_obj = pd;
790 _prop_dictionary_iterator_reset_locked(pdi);
791
792 return (&pdi->pdi_base);
793 }
794
795 /*
796 * prop_dictionary_iterator --
797 * Return an iterator for the dictionary. The dictionary is retained by
798 * the iterator.
799 */
800 prop_object_iterator_t
801 prop_dictionary_iterator(prop_dictionary_t pd)
802 {
803 prop_object_iterator_t pi;
804
805 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
806 pi = _prop_dictionary_iterator_locked(pd);
807 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
808 return (pi);
809 }
810
811 /*
812 * prop_dictionary_all_keys --
813 * Return an array containing a snapshot of all of the keys
814 * in the dictionary.
815 */
816 prop_array_t
817 prop_dictionary_all_keys(prop_dictionary_t pd)
818 {
819 prop_array_t array;
820 unsigned int idx;
821 bool rv = true;
822
823 if (! prop_object_is_dictionary(pd))
824 return (NULL);
825
826 /* There is no pressing need to lock the dictionary for this. */
827 array = prop_array_create_with_capacity(pd->pd_count);
828
829 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
830
831 for (idx = 0; idx < pd->pd_count; idx++) {
832 rv = prop_array_add(array, pd->pd_array[idx].pde_key);
833 if (rv == false)
834 break;
835 }
836
837 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
838
839 if (rv == false) {
840 prop_object_release(array);
841 array = NULL;
842 }
843 return (array);
844 }
845
846 static struct _prop_dict_entry *
847 _prop_dict_lookup(prop_dictionary_t pd, const char *key,
848 unsigned int *idxp)
849 {
850 struct _prop_dict_entry *pde;
851 unsigned int base, idx, distance;
852 int res;
853
854 /*
855 * Dictionary must be READ-LOCKED or WRITE-LOCKED.
856 */
857
858 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
859 distance >>= 1) {
860 idx = base + (distance >> 1);
861 pde = &pd->pd_array[idx];
862 _PROP_ASSERT(pde->pde_key != NULL);
863 res = strcmp(key, pde->pde_key->pdk_key);
864 if (res == 0) {
865 if (idxp != NULL)
866 *idxp = idx;
867 return (pde);
868 }
869 if (res > 0) { /* key > pdk_key: move right */
870 base = idx + 1;
871 distance--;
872 } /* else move left */
873 }
874
875 /* idx points to the slot we looked at last. */
876 if (idxp != NULL)
877 *idxp = idx;
878 return (NULL);
879 }
880
881 static prop_object_t
882 _prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked)
883 {
884 const struct _prop_dict_entry *pde;
885 prop_object_t po = NULL;
886
887 if (! prop_object_is_dictionary(pd))
888 return (NULL);
889
890 if (!locked)
891 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
892 pde = _prop_dict_lookup(pd, key, NULL);
893 if (pde != NULL) {
894 _PROP_ASSERT(pde->pde_objref != NULL);
895 po = pde->pde_objref;
896 }
897 if (!locked)
898 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
899 return (po);
900 }
901 /*
902 * prop_dictionary_get --
903 * Return the object stored with specified key.
904 */
905 prop_object_t
906 prop_dictionary_get(prop_dictionary_t pd, const char *key)
907 {
908 prop_object_t po;
909
910 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
911 po = _prop_dictionary_get(pd, key, true);
912 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
913 return (po);
914 }
915
916 static prop_object_t
917 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
918 bool locked)
919 {
920
921 if (! (prop_object_is_dictionary(pd) &&
922 prop_object_is_dictionary_keysym(pdk)))
923 return (NULL);
924
925 return (_prop_dictionary_get(pd, pdk->pdk_key, locked));
926 }
927
928 /*
929 * prop_dictionary_get_keysym --
930 * Return the object stored at the location encoded by the keysym.
931 */
932 prop_object_t
933 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
934 {
935
936 return (_prop_dictionary_get_keysym(pd, pdk, false));
937 }
938
939 /*
940 * prop_dictionary_set --
941 * Store a reference to an object at with the specified key.
942 * If the key already exisit, the original object is released.
943 */
944 bool
945 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
946 {
947 struct _prop_dict_entry *pde;
948 prop_dictionary_keysym_t pdk;
949 unsigned int idx;
950 bool rv = false;
951
952 if (! prop_object_is_dictionary(pd))
953 return (false);
954
955 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
956
957 if (prop_dictionary_is_immutable(pd))
958 return (false);
959
960 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
961
962 pde = _prop_dict_lookup(pd, key, &idx);
963 if (pde != NULL) {
964 prop_object_t opo = pde->pde_objref;
965 prop_object_retain(po);
966 pde->pde_objref = po;
967 prop_object_release(opo);
968 rv = true;
969 goto out;
970 }
971
972 pdk = _prop_dict_keysym_alloc(key);
973 if (pdk == NULL)
974 goto out;
975
976 if (pd->pd_count == pd->pd_capacity &&
977 _prop_dictionary_expand(pd,
978 pd->pd_capacity + EXPAND_STEP) == false) {
979 prop_object_release(pdk);
980 goto out;
981 }
982
983 /* At this point, the store will succeed. */
984 prop_object_retain(po);
985
986 if (pd->pd_count == 0) {
987 pd->pd_array[0].pde_key = pdk;
988 pd->pd_array[0].pde_objref = po;
989 pd->pd_count++;
990 pd->pd_version++;
991 rv = true;
992 goto out;
993 }
994
995 pde = &pd->pd_array[idx];
996 _PROP_ASSERT(pde->pde_key != NULL);
997
998 if (strcmp(key, pde->pde_key->pdk_key) < 0) {
999 /*
1000 * key < pdk_key: insert to the left. This is the same as
1001 * inserting to the right, except we decrement the current
1002 * index first.
1003 *
1004 * Because we're unsigned, we have to special case 0
1005 * (grumble).
1006 */
1007 if (idx == 0) {
1008 memmove(&pd->pd_array[1], &pd->pd_array[0],
1009 pd->pd_count * sizeof(*pde));
1010 pd->pd_array[0].pde_key = pdk;
1011 pd->pd_array[0].pde_objref = po;
1012 pd->pd_count++;
1013 pd->pd_version++;
1014 rv = true;
1015 goto out;
1016 }
1017 idx--;
1018 }
1019
1020 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
1021 (pd->pd_count - (idx + 1)) * sizeof(*pde));
1022 pd->pd_array[idx + 1].pde_key = pdk;
1023 pd->pd_array[idx + 1].pde_objref = po;
1024 pd->pd_count++;
1025
1026 pd->pd_version++;
1027
1028 rv = true;
1029
1030 out:
1031 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1032 return (rv);
1033 }
1034
1035 /*
1036 * prop_dictionary_set_keysym --
1037 * Replace the object in the dictionary at the location encoded by
1038 * the keysym.
1039 */
1040 bool
1041 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
1042 prop_object_t po)
1043 {
1044
1045 if (! (prop_object_is_dictionary(pd) &&
1046 prop_object_is_dictionary_keysym(pdk)))
1047 return (false);
1048
1049 return (prop_dictionary_set(pd, pdk->pdk_key, po));
1050 }
1051
1052 static void
1053 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
1054 unsigned int idx)
1055 {
1056 prop_dictionary_keysym_t pdk = pde->pde_key;
1057 prop_object_t po = pde->pde_objref;
1058
1059 /*
1060 * Dictionary must be WRITE-LOCKED.
1061 */
1062
1063 _PROP_ASSERT(pd->pd_count != 0);
1064 _PROP_ASSERT(idx < pd->pd_count);
1065 _PROP_ASSERT(pde == &pd->pd_array[idx]);
1066
1067 idx++;
1068 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
1069 (pd->pd_count - idx) * sizeof(*pde));
1070 pd->pd_count--;
1071 pd->pd_version++;
1072
1073 prop_object_release(pdk);
1074 prop_object_release(po);
1075 }
1076
1077 /*
1078 * prop_dictionary_remove --
1079 * Remove the reference to an object with the specified key from
1080 * the dictionary.
1081 */
1082 void
1083 prop_dictionary_remove(prop_dictionary_t pd, const char *key)
1084 {
1085 struct _prop_dict_entry *pde;
1086 unsigned int idx;
1087
1088 if (! prop_object_is_dictionary(pd))
1089 return;
1090
1091 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
1092
1093 /* XXX Should this be a _PROP_ASSERT()? */
1094 if (prop_dictionary_is_immutable(pd))
1095 goto out;
1096
1097 pde = _prop_dict_lookup(pd, key, &idx);
1098 /* XXX Should this be a _PROP_ASSERT()? */
1099 if (pde == NULL)
1100 goto out;
1101
1102 _prop_dictionary_remove(pd, pde, idx);
1103 out:
1104 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
1105 }
1106
1107 /*
1108 * prop_dictionary_remove_keysym --
1109 * Remove a reference to an object stored in the dictionary at the
1110 * location encoded by the keysym.
1111 */
1112 void
1113 prop_dictionary_remove_keysym(prop_dictionary_t pd,
1114 prop_dictionary_keysym_t pdk)
1115 {
1116
1117 if (! (prop_object_is_dictionary(pd) &&
1118 prop_object_is_dictionary_keysym(pdk)))
1119 return;
1120
1121 prop_dictionary_remove(pd, pdk->pdk_key);
1122 }
1123
1124 /*
1125 * prop_dictionary_equals --
1126 * Return true if the two dictionaries are equivalent. Note we do a
1127 * by-value comparison of the objects in the dictionary.
1128 */
1129 bool
1130 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
1131 {
1132 if (!prop_object_is_dictionary(dict1) ||
1133 !prop_object_is_dictionary(dict2))
1134 return (false);
1135
1136 return (prop_object_equals(dict1, dict2));
1137 }
1138
1139 /*
1140 * prop_dictionary_keysym_cstring_nocopy --
1141 * Return an immutable reference to the keysym's value.
1142 */
1143 const char *
1144 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
1145 {
1146
1147 if (! prop_object_is_dictionary_keysym(pdk))
1148 return (NULL);
1149
1150 return (pdk->pdk_key);
1151 }
1152
1153 /*
1154 * prop_dictionary_keysym_equals --
1155 * Return true if the two dictionary key symbols are equivalent.
1156 * Note: We do not compare the object references.
1157 */
1158 bool
1159 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
1160 prop_dictionary_keysym_t pdk2)
1161 {
1162 if (!prop_object_is_dictionary_keysym(pdk1) ||
1163 !prop_object_is_dictionary_keysym(pdk2))
1164 return (_PROP_OBJECT_EQUALS_FALSE);
1165
1166 return (prop_object_equals(pdk1, pdk2));
1167 }
1168
1169 /*
1170 * prop_dictionary_externalize --
1171 * Externalize a dictionary, returning a NUL-terminated buffer
1172 * containing the XML-style representation. The buffer is allocated
1173 * with the M_TEMP memory type.
1174 */
1175 char *
1176 prop_dictionary_externalize(prop_dictionary_t pd)
1177 {
1178 struct _prop_object_externalize_context *ctx;
1179 char *cp;
1180
1181 ctx = _prop_object_externalize_context_alloc();
1182 if (ctx == NULL)
1183 return (NULL);
1184
1185 if (_prop_object_externalize_header(ctx) == false ||
1186 (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false ||
1187 _prop_object_externalize_footer(ctx) == false) {
1188 /* We are responsible for releasing the buffer. */
1189 _PROP_FREE(ctx->poec_buf, M_TEMP);
1190 _prop_object_externalize_context_free(ctx);
1191 return (NULL);
1192 }
1193
1194 cp = ctx->poec_buf;
1195 _prop_object_externalize_context_free(ctx);
1196
1197 return (cp);
1198 }
1199
1200 /*
1201 * _prop_dictionary_internalize --
1202 * Parse a <dict>...</dict> and return the object created from the
1203 * external representation.
1204 *
1205 * Internal state in via rec_data is the storage area for the last processed
1206 * key.
1207 * _prop_dictionary_internalize_body is the upper half of the parse loop.
1208 * It is responsible for parsing the key directly and storing it in the area
1209 * referenced by rec_data.
1210 * _prop_dictionary_internalize_cont is the lower half and called with the value
1211 * associated with the key.
1212 */
1213 static bool _prop_dictionary_internalize_body(prop_stack_t,
1214 prop_object_t *, struct _prop_object_internalize_context *, char *);
1215
1216 bool
1217 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
1218 struct _prop_object_internalize_context *ctx)
1219 {
1220 prop_dictionary_t dict;
1221 char *tmpkey;
1222
1223 /* We don't currently understand any attributes. */
1224 if (ctx->poic_tagattr != NULL)
1225 return (true);
1226
1227 dict = prop_dictionary_create();
1228 if (dict == NULL)
1229 return (true);
1230
1231 if (ctx->poic_is_empty_element) {
1232 *obj = dict;
1233 return (true);
1234 }
1235
1236 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
1237 if (tmpkey == NULL) {
1238 prop_object_release(dict);
1239 return (true);
1240 }
1241
1242 *obj = dict;
1243 /*
1244 * Opening tag is found, storage for key allocated and
1245 * now continue to the first element.
1246 */
1247 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1248 }
1249
1250 static bool
1251 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
1252 struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
1253 {
1254 prop_dictionary_t dict = *obj;
1255 char *tmpkey = data;
1256
1257 _PROP_ASSERT(tmpkey != NULL);
1258
1259 if (child == NULL ||
1260 prop_dictionary_set(dict, tmpkey, child) == false) {
1261 _PROP_FREE(tmpkey, M_TEMP);
1262 if (child != NULL)
1263 prop_object_release(child);
1264 prop_object_release(dict);
1265 *obj = NULL;
1266 return (true);
1267 }
1268
1269 prop_object_release(child);
1270
1271 /*
1272 * key, value was added, now continue looking for the next key
1273 * or the closing tag.
1274 */
1275 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
1276 }
1277
1278 static bool
1279 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
1280 struct _prop_object_internalize_context *ctx, char *tmpkey)
1281 {
1282 prop_dictionary_t dict = *obj;
1283 size_t keylen;
1284
1285 /* Fetch the next tag. */
1286 if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
1287 goto bad;
1288
1289 /* Check to see if this is the end of the dictionary. */
1290 if (_PROP_TAG_MATCH(ctx, "dict") &&
1291 ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
1292 _PROP_FREE(tmpkey, M_TEMP);
1293 return (true);
1294 }
1295
1296 /* Ok, it must be a non-empty key start tag. */
1297 if (!_PROP_TAG_MATCH(ctx, "key") ||
1298 ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
1299 ctx->poic_is_empty_element)
1300 goto bad;
1301
1302 if (_prop_object_internalize_decode_string(ctx,
1303 tmpkey, PDK_MAXKEY, &keylen,
1304 &ctx->poic_cp) == false)
1305 goto bad;
1306
1307 _PROP_ASSERT(keylen <= PDK_MAXKEY);
1308 tmpkey[keylen] = '\0';
1309
1310 if (_prop_object_internalize_find_tag(ctx, "key",
1311 _PROP_TAG_TYPE_END) == false)
1312 goto bad;
1313
1314 /* ..and now the beginning of the value. */
1315 if (_prop_object_internalize_find_tag(ctx, NULL,
1316 _PROP_TAG_TYPE_START) == false)
1317 goto bad;
1318
1319 /*
1320 * Key is found, now wait for value to be parsed.
1321 */
1322 if (_prop_stack_push(stack, *obj,
1323 _prop_dictionary_internalize_continue,
1324 tmpkey, NULL))
1325 return (false);
1326
1327 bad:
1328 _PROP_FREE(tmpkey, M_TEMP);
1329 prop_object_release(dict);
1330 *obj = NULL;
1331 return (true);
1332 }
1333
1334 /*
1335 * prop_dictionary_internalize --
1336 * Create a dictionary by parsing the NUL-terminated XML-style
1337 * representation.
1338 */
1339 prop_dictionary_t
1340 prop_dictionary_internalize(const char *xml)
1341 {
1342 return _prop_generic_internalize(xml, "dict");
1343 }
1344
1345 #if !defined(_KERNEL) && !defined(_STANDALONE)
1346 /*
1347 * prop_dictionary_externalize_to_file --
1348 * Externalize a dictionary to the specified file.
1349 */
1350 bool
1351 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
1352 {
1353 char *xml;
1354 bool rv;
1355 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */
1356
1357 xml = prop_dictionary_externalize(dict);
1358 if (xml == NULL)
1359 return (false);
1360 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
1361 if (rv == false)
1362 save_errno = errno;
1363 _PROP_FREE(xml, M_TEMP);
1364 if (rv == false)
1365 errno = save_errno;
1366
1367 return (rv);
1368 }
1369
1370 /*
1371 * prop_dictionary_internalize_from_file --
1372 * Internalize a dictionary from a file.
1373 */
1374 prop_dictionary_t
1375 prop_dictionary_internalize_from_file(const char *fname)
1376 {
1377 struct _prop_object_internalize_mapped_file *mf;
1378 prop_dictionary_t dict;
1379
1380 mf = _prop_object_internalize_map_file(fname);
1381 if (mf == NULL)
1382 return (NULL);
1383 dict = prop_dictionary_internalize(mf->poimf_xml);
1384 _prop_object_internalize_unmap_file(mf);
1385
1386 return (dict);
1387 }
1388 #endif /* !_KERNEL && !_STANDALONE */
1389