prop_intern.c revision 1.2.4.2 1 1.2.4.2 perseant /* $NetBSD: prop_intern.c,v 1.2.4.2 2025/08/02 05:18:35 perseant Exp $ */
2 1.2.4.2 perseant
3 1.2.4.2 perseant /*-
4 1.2.4.2 perseant * Copyright (c) 2006, 2007, 2025 The NetBSD Foundation, Inc.
5 1.2.4.2 perseant * All rights reserved.
6 1.2.4.2 perseant *
7 1.2.4.2 perseant * This code is derived from software contributed to The NetBSD Foundation
8 1.2.4.2 perseant * by Jason R. Thorpe.
9 1.2.4.2 perseant *
10 1.2.4.2 perseant * Redistribution and use in source and binary forms, with or without
11 1.2.4.2 perseant * modification, are permitted provided that the following conditions
12 1.2.4.2 perseant * are met:
13 1.2.4.2 perseant * 1. Redistributions of source code must retain the above copyright
14 1.2.4.2 perseant * notice, this list of conditions and the following disclaimer.
15 1.2.4.2 perseant * 2. Redistributions in binary form must reproduce the above copyright
16 1.2.4.2 perseant * notice, this list of conditions and the following disclaimer in the
17 1.2.4.2 perseant * documentation and/or other materials provided with the distribution.
18 1.2.4.2 perseant *
19 1.2.4.2 perseant * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.2.4.2 perseant * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.2.4.2 perseant * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.2.4.2 perseant * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.2.4.2 perseant * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.2.4.2 perseant * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.2.4.2 perseant * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.2.4.2 perseant * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.2.4.2 perseant * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.2.4.2 perseant * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.2.4.2 perseant * POSSIBILITY OF SUCH DAMAGE.
30 1.2.4.2 perseant */
31 1.2.4.2 perseant
32 1.2.4.2 perseant #include "prop_object_impl.h"
33 1.2.4.2 perseant #include <prop/prop_object.h>
34 1.2.4.2 perseant
35 1.2.4.2 perseant #if !defined(_KERNEL) && !defined(_STANDALONE)
36 1.2.4.2 perseant #include <sys/mman.h>
37 1.2.4.2 perseant #include <sys/stat.h>
38 1.2.4.2 perseant #include <fcntl.h>
39 1.2.4.2 perseant #include <unistd.h>
40 1.2.4.2 perseant #endif /* !_KERNEL && !_STANDALONE */
41 1.2.4.2 perseant
42 1.2.4.2 perseant /*
43 1.2.4.2 perseant * _prop_intern_skip_whitespace --
44 1.2.4.2 perseant * Skip and span of whitespace.
45 1.2.4.2 perseant */
46 1.2.4.2 perseant const char *
47 1.2.4.2 perseant _prop_intern_skip_whitespace(const char *cp)
48 1.2.4.2 perseant {
49 1.2.4.2 perseant while (_PROP_ISSPACE(*cp)) {
50 1.2.4.2 perseant cp++;
51 1.2.4.2 perseant }
52 1.2.4.2 perseant return cp;
53 1.2.4.2 perseant }
54 1.2.4.2 perseant
55 1.2.4.2 perseant /*
56 1.2.4.2 perseant * _prop_intern_match --
57 1.2.4.2 perseant * Returns true if the two character streams match.
58 1.2.4.2 perseant */
59 1.2.4.2 perseant bool
60 1.2.4.2 perseant _prop_intern_match(const char *str1, size_t len1,
61 1.2.4.2 perseant const char *str2, size_t len2)
62 1.2.4.2 perseant {
63 1.2.4.2 perseant return (len1 == len2 && memcmp(str1, str2, len1) == 0);
64 1.2.4.2 perseant }
65 1.2.4.2 perseant
66 1.2.4.2 perseant /*
67 1.2.4.2 perseant * _prop_xml_intern_skip_comment --
68 1.2.4.2 perseant * Skip the body and end tag of an XML comment.
69 1.2.4.2 perseant */
70 1.2.4.2 perseant static bool
71 1.2.4.2 perseant _prop_xml_intern_skip_comment(struct _prop_object_internalize_context *ctx)
72 1.2.4.2 perseant {
73 1.2.4.2 perseant const char *cp = ctx->poic_cp;
74 1.2.4.2 perseant
75 1.2.4.2 perseant for (cp = ctx->poic_cp; !_PROP_EOF(*cp); cp++) {
76 1.2.4.2 perseant if (cp[0] == '-' &&
77 1.2.4.2 perseant cp[1] == '-' &&
78 1.2.4.2 perseant cp[2] == '>') {
79 1.2.4.2 perseant ctx->poic_cp = cp + 3;
80 1.2.4.2 perseant return true;
81 1.2.4.2 perseant }
82 1.2.4.2 perseant }
83 1.2.4.2 perseant
84 1.2.4.2 perseant return false; /* ran out of buffer */
85 1.2.4.2 perseant }
86 1.2.4.2 perseant
87 1.2.4.2 perseant /*
88 1.2.4.2 perseant * _prop_xml_intern_find_tag --
89 1.2.4.2 perseant * Find the next tag in an XML stream. Optionally compare the found
90 1.2.4.2 perseant * tag to an expected tag name. State of the context is undefined
91 1.2.4.2 perseant * if this routine returns false. Upon success, the context points
92 1.2.4.2 perseant * to the first octet after the tag.
93 1.2.4.2 perseant */
94 1.2.4.2 perseant bool
95 1.2.4.2 perseant _prop_xml_intern_find_tag(struct _prop_object_internalize_context *ctx,
96 1.2.4.2 perseant const char *tag, _prop_tag_type_t type)
97 1.2.4.2 perseant {
98 1.2.4.2 perseant const char *cp;
99 1.2.4.2 perseant size_t taglen;
100 1.2.4.2 perseant
101 1.2.4.2 perseant taglen = tag != NULL ? strlen(tag) : 0;
102 1.2.4.2 perseant
103 1.2.4.2 perseant start_over:
104 1.2.4.2 perseant cp = ctx->poic_cp;
105 1.2.4.2 perseant
106 1.2.4.2 perseant /*
107 1.2.4.2 perseant * Find the start of the tag.
108 1.2.4.2 perseant */
109 1.2.4.2 perseant cp = _prop_intern_skip_whitespace(cp);
110 1.2.4.2 perseant if (*cp != '<') {
111 1.2.4.2 perseant return false;
112 1.2.4.2 perseant }
113 1.2.4.2 perseant
114 1.2.4.2 perseant ctx->poic_tag_start = cp++;
115 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
116 1.2.4.2 perseant return false;
117 1.2.4.2 perseant }
118 1.2.4.2 perseant
119 1.2.4.2 perseant if (*cp == '!') {
120 1.2.4.2 perseant if (cp[1] != '-' || cp[2] != '-') {
121 1.2.4.2 perseant return false;
122 1.2.4.2 perseant }
123 1.2.4.2 perseant /*
124 1.2.4.2 perseant * Comment block -- only allowed if we are allowed to
125 1.2.4.2 perseant * return a start tag.
126 1.2.4.2 perseant */
127 1.2.4.2 perseant if (type == _PROP_TAG_TYPE_END) {
128 1.2.4.2 perseant return false;
129 1.2.4.2 perseant }
130 1.2.4.2 perseant ctx->poic_cp = cp + 3;
131 1.2.4.2 perseant if (_prop_xml_intern_skip_comment(ctx) == false) {
132 1.2.4.2 perseant return false;
133 1.2.4.2 perseant }
134 1.2.4.2 perseant goto start_over;
135 1.2.4.2 perseant }
136 1.2.4.2 perseant
137 1.2.4.2 perseant if (*cp == '/') {
138 1.2.4.2 perseant if (type != _PROP_TAG_TYPE_END &&
139 1.2.4.2 perseant type != _PROP_TAG_TYPE_EITHER) {
140 1.2.4.2 perseant return false;
141 1.2.4.2 perseant }
142 1.2.4.2 perseant cp++;
143 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
144 1.2.4.2 perseant return false;
145 1.2.4.2 perseant }
146 1.2.4.2 perseant ctx->poic_tag_type = _PROP_TAG_TYPE_END;
147 1.2.4.2 perseant } else {
148 1.2.4.2 perseant if (type != _PROP_TAG_TYPE_START &&
149 1.2.4.2 perseant type != _PROP_TAG_TYPE_EITHER) {
150 1.2.4.2 perseant return false;
151 1.2.4.2 perseant }
152 1.2.4.2 perseant ctx->poic_tag_type = _PROP_TAG_TYPE_START;
153 1.2.4.2 perseant }
154 1.2.4.2 perseant
155 1.2.4.2 perseant ctx->poic_tagname = cp;
156 1.2.4.2 perseant
157 1.2.4.2 perseant while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') {
158 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
159 1.2.4.2 perseant return false;
160 1.2.4.2 perseant }
161 1.2.4.2 perseant cp++;
162 1.2.4.2 perseant }
163 1.2.4.2 perseant
164 1.2.4.2 perseant ctx->poic_tagname_len = cp - ctx->poic_tagname;
165 1.2.4.2 perseant
166 1.2.4.2 perseant /* Make sure this is the tag we're looking for. */
167 1.2.4.2 perseant if (tag != NULL &&
168 1.2.4.2 perseant (taglen != ctx->poic_tagname_len ||
169 1.2.4.2 perseant memcmp(tag, ctx->poic_tagname, taglen) != 0)) {
170 1.2.4.2 perseant return false;
171 1.2.4.2 perseant }
172 1.2.4.2 perseant
173 1.2.4.2 perseant /* Check for empty tag. */
174 1.2.4.2 perseant if (*cp == '/') {
175 1.2.4.2 perseant if (ctx->poic_tag_type != _PROP_TAG_TYPE_START) {
176 1.2.4.2 perseant return false; /* only valid on start tags */
177 1.2.4.2 perseant }
178 1.2.4.2 perseant ctx->poic_is_empty_element = true;
179 1.2.4.2 perseant cp++;
180 1.2.4.2 perseant if (_PROP_EOF(*cp) || *cp != '>') {
181 1.2.4.2 perseant return false;
182 1.2.4.2 perseant }
183 1.2.4.2 perseant } else {
184 1.2.4.2 perseant ctx->poic_is_empty_element = false;
185 1.2.4.2 perseant }
186 1.2.4.2 perseant
187 1.2.4.2 perseant /* Easy case of no arguments. */
188 1.2.4.2 perseant if (*cp == '>') {
189 1.2.4.2 perseant ctx->poic_tagattr = NULL;
190 1.2.4.2 perseant ctx->poic_tagattr_len = 0;
191 1.2.4.2 perseant ctx->poic_tagattrval = NULL;
192 1.2.4.2 perseant ctx->poic_tagattrval_len = 0;
193 1.2.4.2 perseant ctx->poic_cp = cp + 1;
194 1.2.4.2 perseant return true;
195 1.2.4.2 perseant }
196 1.2.4.2 perseant
197 1.2.4.2 perseant _PROP_ASSERT(!_PROP_EOF(*cp));
198 1.2.4.2 perseant cp++;
199 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
200 1.2.4.2 perseant return false;
201 1.2.4.2 perseant }
202 1.2.4.2 perseant
203 1.2.4.2 perseant cp = _prop_intern_skip_whitespace(cp);
204 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
205 1.2.4.2 perseant return false;
206 1.2.4.2 perseant }
207 1.2.4.2 perseant
208 1.2.4.2 perseant ctx->poic_tagattr = cp;
209 1.2.4.2 perseant
210 1.2.4.2 perseant while (!_PROP_ISSPACE(*cp) && *cp != '=') {
211 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
212 1.2.4.2 perseant return false;
213 1.2.4.2 perseant }
214 1.2.4.2 perseant cp++;
215 1.2.4.2 perseant }
216 1.2.4.2 perseant
217 1.2.4.2 perseant ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
218 1.2.4.2 perseant
219 1.2.4.2 perseant cp++;
220 1.2.4.2 perseant if (*cp != '\"') {
221 1.2.4.2 perseant return false;
222 1.2.4.2 perseant }
223 1.2.4.2 perseant cp++;
224 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
225 1.2.4.2 perseant return false;
226 1.2.4.2 perseant }
227 1.2.4.2 perseant
228 1.2.4.2 perseant ctx->poic_tagattrval = cp;
229 1.2.4.2 perseant while (*cp != '\"') {
230 1.2.4.2 perseant if (_PROP_EOF(*cp)) {
231 1.2.4.2 perseant return false;
232 1.2.4.2 perseant }
233 1.2.4.2 perseant cp++;
234 1.2.4.2 perseant }
235 1.2.4.2 perseant ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
236 1.2.4.2 perseant
237 1.2.4.2 perseant cp++;
238 1.2.4.2 perseant if (*cp != '>') {
239 1.2.4.2 perseant return false;
240 1.2.4.2 perseant }
241 1.2.4.2 perseant
242 1.2.4.2 perseant ctx->poic_cp = cp + 1;
243 1.2.4.2 perseant return true;
244 1.2.4.2 perseant }
245 1.2.4.2 perseant
246 1.2.4.2 perseant #define INTERNALIZER(t, f) \
247 1.2.4.2 perseant { t, sizeof(t) - 1, f }
248 1.2.4.2 perseant
249 1.2.4.2 perseant static const struct _prop_object_internalizer {
250 1.2.4.2 perseant const char *poi_tag;
251 1.2.4.2 perseant size_t poi_taglen;
252 1.2.4.2 perseant prop_object_internalizer_t poi_intern;
253 1.2.4.2 perseant } _prop_object_internalizer_table[] = {
254 1.2.4.2 perseant INTERNALIZER("array", _prop_array_internalize),
255 1.2.4.2 perseant
256 1.2.4.2 perseant INTERNALIZER("true", _prop_bool_internalize),
257 1.2.4.2 perseant INTERNALIZER("false", _prop_bool_internalize),
258 1.2.4.2 perseant
259 1.2.4.2 perseant INTERNALIZER("data", _prop_data_internalize),
260 1.2.4.2 perseant
261 1.2.4.2 perseant INTERNALIZER("dict", _prop_dictionary_internalize),
262 1.2.4.2 perseant
263 1.2.4.2 perseant INTERNALIZER("integer", _prop_number_internalize),
264 1.2.4.2 perseant
265 1.2.4.2 perseant INTERNALIZER("string", _prop_string_internalize),
266 1.2.4.2 perseant
267 1.2.4.2 perseant { 0, 0, NULL }
268 1.2.4.2 perseant };
269 1.2.4.2 perseant
270 1.2.4.2 perseant #undef INTERNALIZER
271 1.2.4.2 perseant
272 1.2.4.2 perseant /*
273 1.2.4.2 perseant * _prop_xml_intern_by_tag --
274 1.2.4.2 perseant * Determine the object type from the tag in the context and
275 1.2.4.2 perseant * internalize it.
276 1.2.4.2 perseant */
277 1.2.4.2 perseant static prop_object_t
278 1.2.4.2 perseant _prop_xml_intern_by_tag(struct _prop_object_internalize_context *ctx)
279 1.2.4.2 perseant {
280 1.2.4.2 perseant const struct _prop_object_internalizer *poi;
281 1.2.4.2 perseant prop_object_t obj, parent_obj;
282 1.2.4.2 perseant void *data, *iter;
283 1.2.4.2 perseant prop_object_internalizer_continue_t iter_func;
284 1.2.4.2 perseant struct _prop_stack stack;
285 1.2.4.2 perseant
286 1.2.4.2 perseant _prop_stack_init(&stack);
287 1.2.4.2 perseant
288 1.2.4.2 perseant match_start:
289 1.2.4.2 perseant for (poi = _prop_object_internalizer_table;
290 1.2.4.2 perseant poi->poi_tag != NULL; poi++) {
291 1.2.4.2 perseant if (_prop_intern_match(ctx->poic_tagname,
292 1.2.4.2 perseant ctx->poic_tagname_len,
293 1.2.4.2 perseant poi->poi_tag,
294 1.2.4.2 perseant poi->poi_taglen)) {
295 1.2.4.2 perseant break;
296 1.2.4.2 perseant }
297 1.2.4.2 perseant }
298 1.2.4.2 perseant if (poi == NULL || poi->poi_tag == NULL) {
299 1.2.4.2 perseant while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
300 1.2.4.2 perseant iter_func = (prop_object_internalizer_continue_t)iter;
301 1.2.4.2 perseant (*iter_func)(&stack, &obj, ctx, data, NULL);
302 1.2.4.2 perseant }
303 1.2.4.2 perseant return NULL;
304 1.2.4.2 perseant }
305 1.2.4.2 perseant
306 1.2.4.2 perseant obj = NULL;
307 1.2.4.2 perseant if (!(*poi->poi_intern)(&stack, &obj, ctx)) {
308 1.2.4.2 perseant goto match_start;
309 1.2.4.2 perseant }
310 1.2.4.2 perseant
311 1.2.4.2 perseant parent_obj = obj;
312 1.2.4.2 perseant while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
313 1.2.4.2 perseant iter_func = (prop_object_internalizer_continue_t)iter;
314 1.2.4.2 perseant if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj)) {
315 1.2.4.2 perseant goto match_start;
316 1.2.4.2 perseant }
317 1.2.4.2 perseant obj = parent_obj;
318 1.2.4.2 perseant }
319 1.2.4.2 perseant
320 1.2.4.2 perseant return parent_obj;
321 1.2.4.2 perseant }
322 1.2.4.2 perseant
323 1.2.4.2 perseant #define ADDCHAR(x) \
324 1.2.4.2 perseant do { \
325 1.2.4.2 perseant if (target) { \
326 1.2.4.2 perseant if (tarindex >= targsize) { \
327 1.2.4.2 perseant return false; \
328 1.2.4.2 perseant } \
329 1.2.4.2 perseant target[tarindex] = (x); \
330 1.2.4.2 perseant } \
331 1.2.4.2 perseant tarindex++; \
332 1.2.4.2 perseant } while (/*CONSTCOND*/0)
333 1.2.4.2 perseant
334 1.2.4.2 perseant /*
335 1.2.4.2 perseant * _prop_json_intern_decode_uesc_getu16 --
336 1.2.4.2 perseant * Get the 16-bit value from a "u-escape" ("\uXXXX").
337 1.2.4.2 perseant */
338 1.2.4.2 perseant static unsigned int
339 1.2.4.2 perseant _prop_json_intern_decode_uesc_getu16(const char *src, unsigned int idx,
340 1.2.4.2 perseant uint16_t *valp)
341 1.2.4.2 perseant {
342 1.2.4.2 perseant unsigned int i;
343 1.2.4.2 perseant uint16_t val;
344 1.2.4.2 perseant unsigned char c;
345 1.2.4.2 perseant
346 1.2.4.2 perseant if (src[idx] != '\\' || src[idx + 1] != 'u') {
347 1.2.4.2 perseant return 0;
348 1.2.4.2 perseant }
349 1.2.4.2 perseant
350 1.2.4.2 perseant for (val = 0, i = 2; i < 6; i++) {
351 1.2.4.2 perseant val <<= 4;
352 1.2.4.2 perseant c = src[idx + i];
353 1.2.4.2 perseant if (c >= 'A' && c <= 'F') {
354 1.2.4.2 perseant val |= 10 + (c - 'A');
355 1.2.4.2 perseant } else if (c >= 'a' && c <= 'f') {
356 1.2.4.2 perseant val |= 10 + (c - 'a');
357 1.2.4.2 perseant } else if (c >= '0' && c <= '9') {
358 1.2.4.2 perseant val |= c - '0';
359 1.2.4.2 perseant } else {
360 1.2.4.2 perseant return 0;
361 1.2.4.2 perseant }
362 1.2.4.2 perseant }
363 1.2.4.2 perseant
364 1.2.4.2 perseant *valp = val;
365 1.2.4.2 perseant return idx + i;
366 1.2.4.2 perseant }
367 1.2.4.2 perseant
368 1.2.4.2 perseant #define HS_FIRST 0xd800
369 1.2.4.2 perseant #define HS_LAST 0xdbff
370 1.2.4.2 perseant #define HS_SHIFT 10
371 1.2.4.2 perseant #define LS_FIRST 0xdc00
372 1.2.4.2 perseant #define LS_LAST 0xdfff
373 1.2.4.2 perseant
374 1.2.4.2 perseant #define HIGH_SURROGAGE_P(x) \
375 1.2.4.2 perseant ((x) >= HS_FIRST && (x) <= HS_LAST)
376 1.2.4.2 perseant #define LOW_SURROGATE_P(x) \
377 1.2.4.2 perseant ((x) >= LS_FIRST && (x) <= LS_LAST)
378 1.2.4.2 perseant #define SURROGATE_P(x) \
379 1.2.4.2 perseant (HIGH_SURROGAGE_P(x) || LOW_SURROGATE_P(x))
380 1.2.4.2 perseant
381 1.2.4.2 perseant /*
382 1.2.4.2 perseant * _prop_json_intern_decode_uesc --
383 1.2.4.2 perseant * Decode a JSON UTF-16 "u-escape" ("\uXXXX").
384 1.2.4.2 perseant */
385 1.2.4.2 perseant static int
386 1.2.4.2 perseant _prop_json_intern_decode_uesc(const char *src, char *c, unsigned int *cszp)
387 1.2.4.2 perseant {
388 1.2.4.2 perseant unsigned int idx = 0;
389 1.2.4.2 perseant uint32_t code;
390 1.2.4.2 perseant uint16_t code16[2] = { 0, 0 };
391 1.2.4.2 perseant
392 1.2.4.2 perseant idx = _prop_json_intern_decode_uesc_getu16(src, idx, &code16[0]);
393 1.2.4.2 perseant if (idx == 0) {
394 1.2.4.2 perseant return 0;
395 1.2.4.2 perseant }
396 1.2.4.2 perseant if (! SURROGATE_P(code16[0])) {
397 1.2.4.2 perseant /* Simple case: not a surrogate pair */
398 1.2.4.2 perseant code = code16[0];
399 1.2.4.2 perseant } else if (HIGH_SURROGAGE_P(code16[0])) {
400 1.2.4.2 perseant idx = _prop_json_intern_decode_uesc_getu16(src, idx,
401 1.2.4.2 perseant &code16[1]);
402 1.2.4.2 perseant if (idx == 0) {
403 1.2.4.2 perseant return 0;
404 1.2.4.2 perseant }
405 1.2.4.2 perseant /* Next code must be the low surrogate. */
406 1.2.4.2 perseant if (! LOW_SURROGATE_P(code16[1])) {
407 1.2.4.2 perseant return 0;
408 1.2.4.2 perseant }
409 1.2.4.2 perseant code = (((uint32_t)code16[0] - HS_FIRST) << HS_SHIFT) +
410 1.2.4.2 perseant ( code16[1] - LS_FIRST) +
411 1.2.4.2 perseant 0x10000;
412 1.2.4.2 perseant } else {
413 1.2.4.2 perseant /* Got the low surrogate first; this is an error. */
414 1.2.4.2 perseant return 0;
415 1.2.4.2 perseant }
416 1.2.4.2 perseant
417 1.2.4.2 perseant /*
418 1.2.4.2 perseant * Ok, we have the code point. Now convert it to UTF-8.
419 1.2.4.2 perseant * First we'll just split into nybbles.
420 1.2.4.2 perseant */
421 1.2.4.2 perseant uint8_t u = (code >> 20) & 0xf;
422 1.2.4.2 perseant uint8_t v = (code >> 16) & 0xf;
423 1.2.4.2 perseant uint8_t w = (code >> 12) & 0xf;
424 1.2.4.2 perseant uint8_t x = (code >> 8) & 0xf;
425 1.2.4.2 perseant uint8_t y = (code >> 4) & 0xf;
426 1.2.4.2 perseant uint8_t z = (code ) & 0xf;
427 1.2.4.2 perseant
428 1.2.4.2 perseant /*
429 1.2.4.2 perseant * ...and swizzle the nybbles accordingly.
430 1.2.4.2 perseant *
431 1.2.4.2 perseant * N.B. we expcitly disallow inserting a NUL into the string
432 1.2.4.2 perseant * by way of a \uXXXX escape.
433 1.2.4.2 perseant */
434 1.2.4.2 perseant if (code == 0) {
435 1.2.4.2 perseant /* Not allowed. */
436 1.2.4.2 perseant return 0;
437 1.2.4.2 perseant } else if (/*code >= 0x0000 &&*/ code <= 0x007f) {
438 1.2.4.2 perseant c[0] = (char)code; /* == (y << 4) | z */
439 1.2.4.2 perseant *cszp = 1;
440 1.2.4.2 perseant } else if (/*code >= 0x0080 &&*/ code <= 0x07ff) {
441 1.2.4.2 perseant c[0] = 0xc0 | (x << 2) | (y >> 2);
442 1.2.4.2 perseant c[1] = 0x80 | ((y & 3) << 4) | z;
443 1.2.4.2 perseant *cszp = 2;
444 1.2.4.2 perseant } else if (/*code >= 0x0800 &&*/ code <= 0xffff) {
445 1.2.4.2 perseant c[0] = 0xe0 | w;
446 1.2.4.2 perseant c[1] = 0x80 | (x << 2) | (y >> 2);
447 1.2.4.2 perseant c[2] = 0x80 | ((y & 3) << 4) | z;
448 1.2.4.2 perseant *cszp = 3;
449 1.2.4.2 perseant } else if (/*code >= 0x010000 &&*/ code <= 0x10ffff) {
450 1.2.4.2 perseant c[0] = 0xf0 | ((u & 1) << 2) | (v >> 2);
451 1.2.4.2 perseant c[1] = 0x80 | ((v & 3) << 4) | w;
452 1.2.4.2 perseant c[2] = 0x80 | (x << 2) | (y >> 2);
453 1.2.4.2 perseant c[3] = 0x80 | ((y & 3) << 4) | z;
454 1.2.4.2 perseant *cszp = 4;
455 1.2.4.2 perseant } else {
456 1.2.4.2 perseant /* Invalid code. */
457 1.2.4.2 perseant return 0;
458 1.2.4.2 perseant }
459 1.2.4.2 perseant
460 1.2.4.2 perseant return idx; /* advance input by this much */
461 1.2.4.2 perseant }
462 1.2.4.2 perseant
463 1.2.4.2 perseant #undef HS_FIRST
464 1.2.4.2 perseant #undef HS_LAST
465 1.2.4.2 perseant #undef LS_FIRST
466 1.2.4.2 perseant #undef LS_LAST
467 1.2.4.2 perseant #undef HIGH_SURROGAGE_P
468 1.2.4.2 perseant #undef LOW_SURROGATE_P
469 1.2.4.2 perseant #undef SURROGATE_P
470 1.2.4.2 perseant
471 1.2.4.2 perseant /*
472 1.2.4.2 perseant * _prop_json_intern_decode_string --
473 1.2.4.2 perseant * Decode a JSON-encoded string.
474 1.2.4.2 perseant */
475 1.2.4.2 perseant static bool
476 1.2.4.2 perseant _prop_json_intern_decode_string(struct _prop_object_internalize_context *ctx,
477 1.2.4.2 perseant char *target, size_t targsize, size_t *sizep,
478 1.2.4.2 perseant const char **cpp)
479 1.2.4.2 perseant {
480 1.2.4.2 perseant const char *src;
481 1.2.4.2 perseant size_t tarindex;
482 1.2.4.2 perseant char c[4];
483 1.2.4.2 perseant unsigned int csz;
484 1.2.4.2 perseant
485 1.2.4.2 perseant tarindex = 0;
486 1.2.4.2 perseant src = ctx->poic_cp;
487 1.2.4.2 perseant
488 1.2.4.2 perseant for (;;) {
489 1.2.4.2 perseant if (_PROP_EOF(*src)) {
490 1.2.4.2 perseant return false;
491 1.2.4.2 perseant }
492 1.2.4.2 perseant if (*src == '"') {
493 1.2.4.2 perseant break;
494 1.2.4.2 perseant }
495 1.2.4.2 perseant
496 1.2.4.2 perseant csz = 1;
497 1.2.4.2 perseant if ((c[0] = *src) == '\\') {
498 1.2.4.2 perseant int advance = 2;
499 1.2.4.2 perseant
500 1.2.4.2 perseant switch ((c[0] = src[1])) {
501 1.2.4.2 perseant case '"': /* quotation mark */
502 1.2.4.2 perseant case '\\': /* reverse solidus */
503 1.2.4.2 perseant case '/': /* solidus */
504 1.2.4.2 perseant /* identity mapping */
505 1.2.4.2 perseant break;
506 1.2.4.2 perseant
507 1.2.4.2 perseant case 'b': /* backspace */
508 1.2.4.2 perseant c[0] = 0x08;
509 1.2.4.2 perseant break;
510 1.2.4.2 perseant
511 1.2.4.2 perseant case 'f': /* form feed */
512 1.2.4.2 perseant c[0] = 0x0c;
513 1.2.4.2 perseant break;
514 1.2.4.2 perseant
515 1.2.4.2 perseant case 'n': /* line feed */
516 1.2.4.2 perseant c[0] = 0x0a;
517 1.2.4.2 perseant break;
518 1.2.4.2 perseant
519 1.2.4.2 perseant case 'r': /* carriage return */
520 1.2.4.2 perseant c[0] = 0x0d;
521 1.2.4.2 perseant break;
522 1.2.4.2 perseant
523 1.2.4.2 perseant case 't': /* tab */
524 1.2.4.2 perseant c[0] = 0x09;
525 1.2.4.2 perseant break;
526 1.2.4.2 perseant
527 1.2.4.2 perseant case 'u':
528 1.2.4.2 perseant advance = _prop_json_intern_decode_uesc(
529 1.2.4.2 perseant src, c, &csz);
530 1.2.4.2 perseant if (advance == 0) {
531 1.2.4.2 perseant return false;
532 1.2.4.2 perseant }
533 1.2.4.2 perseant break;
534 1.2.4.2 perseant
535 1.2.4.2 perseant default:
536 1.2.4.2 perseant /* invalid escape */
537 1.2.4.2 perseant return false;
538 1.2.4.2 perseant }
539 1.2.4.2 perseant src += advance;
540 1.2.4.2 perseant } else {
541 1.2.4.2 perseant src++;
542 1.2.4.2 perseant }
543 1.2.4.2 perseant for (unsigned int i = 0; i < csz; i++) {
544 1.2.4.2 perseant ADDCHAR(c[i]);
545 1.2.4.2 perseant }
546 1.2.4.2 perseant }
547 1.2.4.2 perseant
548 1.2.4.2 perseant _PROP_ASSERT(*src == '"');
549 1.2.4.2 perseant if (sizep != NULL) {
550 1.2.4.2 perseant *sizep = tarindex;
551 1.2.4.2 perseant }
552 1.2.4.2 perseant if (cpp != NULL) {
553 1.2.4.2 perseant *cpp = src;
554 1.2.4.2 perseant }
555 1.2.4.2 perseant
556 1.2.4.2 perseant return true;
557 1.2.4.2 perseant }
558 1.2.4.2 perseant
559 1.2.4.2 perseant /*
560 1.2.4.2 perseant * _prop_xml_intern_decode_string --
561 1.2.4.2 perseant * Decode an XML-encoded string.
562 1.2.4.2 perseant */
563 1.2.4.2 perseant static bool
564 1.2.4.2 perseant _prop_xml_intern_decode_string(struct _prop_object_internalize_context *ctx,
565 1.2.4.2 perseant char *target, size_t targsize, size_t *sizep,
566 1.2.4.2 perseant const char **cpp)
567 1.2.4.2 perseant {
568 1.2.4.2 perseant const char *src;
569 1.2.4.2 perseant size_t tarindex;
570 1.2.4.2 perseant char c;
571 1.2.4.2 perseant
572 1.2.4.2 perseant tarindex = 0;
573 1.2.4.2 perseant src = ctx->poic_cp;
574 1.2.4.2 perseant
575 1.2.4.2 perseant for (;;) {
576 1.2.4.2 perseant if (_PROP_EOF(*src)) {
577 1.2.4.2 perseant return true;
578 1.2.4.2 perseant }
579 1.2.4.2 perseant if (*src == '<') {
580 1.2.4.2 perseant break;
581 1.2.4.2 perseant }
582 1.2.4.2 perseant
583 1.2.4.2 perseant if ((c = *src) == '&') {
584 1.2.4.2 perseant if (src[1] == 'a' &&
585 1.2.4.2 perseant src[2] == 'm' &&
586 1.2.4.2 perseant src[3] == 'p' &&
587 1.2.4.2 perseant src[4] == ';') {
588 1.2.4.2 perseant c = '&';
589 1.2.4.2 perseant src += 5;
590 1.2.4.2 perseant } else if (src[1] == 'l' &&
591 1.2.4.2 perseant src[2] == 't' &&
592 1.2.4.2 perseant src[3] == ';') {
593 1.2.4.2 perseant c = '<';
594 1.2.4.2 perseant src += 4;
595 1.2.4.2 perseant } else if (src[1] == 'g' &&
596 1.2.4.2 perseant src[2] == 't' &&
597 1.2.4.2 perseant src[3] == ';') {
598 1.2.4.2 perseant c = '>';
599 1.2.4.2 perseant src += 4;
600 1.2.4.2 perseant } else if (src[1] == 'a' &&
601 1.2.4.2 perseant src[2] == 'p' &&
602 1.2.4.2 perseant src[3] == 'o' &&
603 1.2.4.2 perseant src[4] == 's' &&
604 1.2.4.2 perseant src[5] == ';') {
605 1.2.4.2 perseant c = '\'';
606 1.2.4.2 perseant src += 6;
607 1.2.4.2 perseant } else if (src[1] == 'q' &&
608 1.2.4.2 perseant src[2] == 'u' &&
609 1.2.4.2 perseant src[3] == 'o' &&
610 1.2.4.2 perseant src[4] == 't' &&
611 1.2.4.2 perseant src[5] == ';') {
612 1.2.4.2 perseant c = '\"';
613 1.2.4.2 perseant src += 6;
614 1.2.4.2 perseant } else {
615 1.2.4.2 perseant return false;
616 1.2.4.2 perseant }
617 1.2.4.2 perseant } else {
618 1.2.4.2 perseant src++;
619 1.2.4.2 perseant }
620 1.2.4.2 perseant ADDCHAR(c);
621 1.2.4.2 perseant }
622 1.2.4.2 perseant
623 1.2.4.2 perseant _PROP_ASSERT(*src == '<');
624 1.2.4.2 perseant if (sizep != NULL) {
625 1.2.4.2 perseant *sizep = tarindex;
626 1.2.4.2 perseant }
627 1.2.4.2 perseant if (cpp != NULL) {
628 1.2.4.2 perseant *cpp = src;
629 1.2.4.2 perseant }
630 1.2.4.2 perseant
631 1.2.4.2 perseant return true;
632 1.2.4.2 perseant }
633 1.2.4.2 perseant
634 1.2.4.2 perseant #undef ADDCHAR
635 1.2.4.2 perseant
636 1.2.4.2 perseant /*
637 1.2.4.2 perseant * _prop_intern_decode_string --
638 1.2.4.2 perseant * Decode an encoded string.
639 1.2.4.2 perseant */
640 1.2.4.2 perseant bool
641 1.2.4.2 perseant _prop_intern_decode_string(struct _prop_object_internalize_context *ctx,
642 1.2.4.2 perseant char *target, size_t targsize, size_t *sizep,
643 1.2.4.2 perseant const char **cpp)
644 1.2.4.2 perseant {
645 1.2.4.2 perseant _PROP_ASSERT(ctx->poic_format == PROP_FORMAT_XML ||
646 1.2.4.2 perseant ctx->poic_format == PROP_FORMAT_JSON);
647 1.2.4.2 perseant
648 1.2.4.2 perseant switch (ctx->poic_format) {
649 1.2.4.2 perseant case PROP_FORMAT_JSON:
650 1.2.4.2 perseant return _prop_json_intern_decode_string(ctx, target, targsize,
651 1.2.4.2 perseant sizep, cpp);
652 1.2.4.2 perseant
653 1.2.4.2 perseant default: /* PROP_FORMAT_XML */
654 1.2.4.2 perseant return _prop_xml_intern_decode_string(ctx, target, targsize,
655 1.2.4.2 perseant sizep, cpp);
656 1.2.4.2 perseant }
657 1.2.4.2 perseant }
658 1.2.4.2 perseant
659 1.2.4.2 perseant /*
660 1.2.4.2 perseant * _prop_intern_context_alloc --
661 1.2.4.2 perseant * Allocate an internalize context.
662 1.2.4.2 perseant */
663 1.2.4.2 perseant static struct _prop_object_internalize_context *
664 1.2.4.2 perseant _prop_intern_context_alloc(const char *data, prop_format_t fmt)
665 1.2.4.2 perseant {
666 1.2.4.2 perseant struct _prop_object_internalize_context *ctx;
667 1.2.4.2 perseant
668 1.2.4.2 perseant ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
669 1.2.4.2 perseant if (ctx == NULL) {
670 1.2.4.2 perseant return NULL;
671 1.2.4.2 perseant }
672 1.2.4.2 perseant
673 1.2.4.2 perseant ctx->poic_format = fmt;
674 1.2.4.2 perseant ctx->poic_data = ctx->poic_cp = data;
675 1.2.4.2 perseant
676 1.2.4.2 perseant /*
677 1.2.4.2 perseant * If we're digesting JSON, check for a byte order mark and
678 1.2.4.2 perseant * skip it, if present. We should never see one, but we're
679 1.2.4.2 perseant * allowed to detect and ignore it. (RFC 8259 section 8.1)
680 1.2.4.2 perseant */
681 1.2.4.2 perseant if (fmt == PROP_FORMAT_JSON) {
682 1.2.4.2 perseant if (((unsigned char)data[0] == 0xff &&
683 1.2.4.2 perseant (unsigned char)data[1] == 0xfe) ||
684 1.2.4.2 perseant ((unsigned char)data[0] == 0xfe &&
685 1.2.4.2 perseant (unsigned char)data[1] == 0xff)) {
686 1.2.4.2 perseant ctx->poic_cp = data + 2;
687 1.2.4.2 perseant }
688 1.2.4.2 perseant
689 1.2.4.2 perseant /* No additional processing work to do for JSON. */
690 1.2.4.2 perseant return ctx;
691 1.2.4.2 perseant }
692 1.2.4.2 perseant
693 1.2.4.2 perseant /*
694 1.2.4.2 perseant * Skip any whitespace and XML preamble stuff that we don't
695 1.2.4.2 perseant * know about / care about.
696 1.2.4.2 perseant */
697 1.2.4.2 perseant for (;;) {
698 1.2.4.2 perseant data = _prop_intern_skip_whitespace(data);
699 1.2.4.2 perseant if (_PROP_EOF(*data) || *data != '<') {
700 1.2.4.2 perseant goto bad;
701 1.2.4.2 perseant }
702 1.2.4.2 perseant
703 1.2.4.2 perseant #define MATCH(str) (strncmp(&data[1], str, strlen(str)) == 0)
704 1.2.4.2 perseant
705 1.2.4.2 perseant /*
706 1.2.4.2 perseant * Skip over the XML preamble that Apple XML property
707 1.2.4.2 perseant * lists usually include at the top of the file.
708 1.2.4.2 perseant */
709 1.2.4.2 perseant if (MATCH("?xml ") ||
710 1.2.4.2 perseant MATCH("!DOCTYPE plist")) {
711 1.2.4.2 perseant while (*data != '>' && !_PROP_EOF(*data)) {
712 1.2.4.2 perseant data++;
713 1.2.4.2 perseant }
714 1.2.4.2 perseant if (_PROP_EOF(*data)) {
715 1.2.4.2 perseant goto bad;
716 1.2.4.2 perseant }
717 1.2.4.2 perseant data++; /* advance past the '>' */
718 1.2.4.2 perseant continue;
719 1.2.4.2 perseant }
720 1.2.4.2 perseant
721 1.2.4.2 perseant if (MATCH("<!--")) {
722 1.2.4.2 perseant ctx->poic_cp = data + 4;
723 1.2.4.2 perseant if (_prop_xml_intern_skip_comment(ctx) == false) {
724 1.2.4.2 perseant goto bad;
725 1.2.4.2 perseant }
726 1.2.4.2 perseant data = ctx->poic_cp;
727 1.2.4.2 perseant continue;
728 1.2.4.2 perseant }
729 1.2.4.2 perseant
730 1.2.4.2 perseant #undef MATCH
731 1.2.4.2 perseant
732 1.2.4.2 perseant /*
733 1.2.4.2 perseant * We don't think we should skip it, so let's hope we can
734 1.2.4.2 perseant * parse it.
735 1.2.4.2 perseant */
736 1.2.4.2 perseant break;
737 1.2.4.2 perseant }
738 1.2.4.2 perseant
739 1.2.4.2 perseant ctx->poic_cp = data;
740 1.2.4.2 perseant return ctx;
741 1.2.4.2 perseant bad:
742 1.2.4.2 perseant _PROP_FREE(ctx, M_TEMP);
743 1.2.4.2 perseant return NULL;
744 1.2.4.2 perseant }
745 1.2.4.2 perseant
746 1.2.4.2 perseant /*
747 1.2.4.2 perseant * _prop_intern_context_free --
748 1.2.4.2 perseant * Free an internalize context.
749 1.2.4.2 perseant */
750 1.2.4.2 perseant static void
751 1.2.4.2 perseant _prop_intern_context_free(struct _prop_object_internalize_context *ctx)
752 1.2.4.2 perseant {
753 1.2.4.2 perseant _PROP_FREE(ctx, M_TEMP);
754 1.2.4.2 perseant }
755 1.2.4.2 perseant
756 1.2.4.2 perseant /*
757 1.2.4.2 perseant * _prop_object_internalize_json --
758 1.2.4.2 perseant * Internalize a property list from JSON data.
759 1.2.4.2 perseant */
760 1.2.4.2 perseant static prop_object_t
761 1.2.4.2 perseant _prop_object_internalize_json(struct _prop_object_internalize_context *ctx,
762 1.2.4.2 perseant const struct _prop_object_type_tags *initial_tag __unused)
763 1.2.4.2 perseant {
764 1.2.4.2 perseant prop_object_t obj, parent_obj;
765 1.2.4.2 perseant void *data, *iter;
766 1.2.4.2 perseant prop_object_internalizer_continue_t iter_func;
767 1.2.4.2 perseant struct _prop_stack stack;
768 1.2.4.2 perseant bool (*intern)(prop_stack_t, prop_object_t *,
769 1.2.4.2 perseant struct _prop_object_internalize_context *);
770 1.2.4.2 perseant
771 1.2.4.2 perseant _prop_stack_init(&stack);
772 1.2.4.2 perseant
773 1.2.4.2 perseant match_start:
774 1.2.4.2 perseant intern = NULL;
775 1.2.4.2 perseant ctx->poic_tagname = ctx->poic_tagattr = ctx->poic_tagattrval = NULL;
776 1.2.4.2 perseant ctx->poic_tagname_len = ctx->poic_tagattr_len =
777 1.2.4.2 perseant ctx->poic_tagattrval_len = 0;
778 1.2.4.2 perseant ctx->poic_is_empty_element = false;
779 1.2.4.2 perseant ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp);
780 1.2.4.2 perseant switch (ctx->poic_cp[0]) {
781 1.2.4.2 perseant case '{':
782 1.2.4.2 perseant ctx->poic_cp++;
783 1.2.4.2 perseant intern = _prop_dictionary_internalize;
784 1.2.4.2 perseant break;
785 1.2.4.2 perseant
786 1.2.4.2 perseant case '[':
787 1.2.4.2 perseant ctx->poic_cp++;
788 1.2.4.2 perseant intern = _prop_array_internalize;
789 1.2.4.2 perseant break;
790 1.2.4.2 perseant
791 1.2.4.2 perseant case '"':
792 1.2.4.2 perseant ctx->poic_cp++;
793 1.2.4.2 perseant /* XXX Slightly gross. */
794 1.2.4.2 perseant if (*ctx->poic_cp == '"') {
795 1.2.4.2 perseant ctx->poic_cp++;
796 1.2.4.2 perseant ctx->poic_is_empty_element = true;
797 1.2.4.2 perseant }
798 1.2.4.2 perseant intern = _prop_string_internalize;
799 1.2.4.2 perseant break;
800 1.2.4.2 perseant
801 1.2.4.2 perseant case 't':
802 1.2.4.2 perseant if (ctx->poic_cp[1] == 'r' &&
803 1.2.4.2 perseant ctx->poic_cp[2] == 'u' &&
804 1.2.4.2 perseant ctx->poic_cp[3] == 'e') {
805 1.2.4.2 perseant /* XXX Slightly gross. */
806 1.2.4.2 perseant ctx->poic_tagname = ctx->poic_cp;
807 1.2.4.2 perseant ctx->poic_tagname_len = 4;
808 1.2.4.2 perseant ctx->poic_is_empty_element = true;
809 1.2.4.2 perseant intern = _prop_bool_internalize;
810 1.2.4.2 perseant ctx->poic_cp += 4;
811 1.2.4.2 perseant }
812 1.2.4.2 perseant break;
813 1.2.4.2 perseant
814 1.2.4.2 perseant case 'f':
815 1.2.4.2 perseant if (ctx->poic_cp[1] == 'a' &&
816 1.2.4.2 perseant ctx->poic_cp[2] == 'l' &&
817 1.2.4.2 perseant ctx->poic_cp[3] == 's' &&
818 1.2.4.2 perseant ctx->poic_cp[4] == 'e') {
819 1.2.4.2 perseant /* XXX Slightly gross. */
820 1.2.4.2 perseant ctx->poic_tagname = ctx->poic_cp;
821 1.2.4.2 perseant ctx->poic_tagname_len = 5;
822 1.2.4.2 perseant ctx->poic_is_empty_element = true;
823 1.2.4.2 perseant intern = _prop_bool_internalize;
824 1.2.4.2 perseant ctx->poic_cp += 5;
825 1.2.4.2 perseant }
826 1.2.4.2 perseant break;
827 1.2.4.2 perseant
828 1.2.4.2 perseant default:
829 1.2.4.2 perseant if (ctx->poic_cp[0] == '+' ||
830 1.2.4.2 perseant ctx->poic_cp[0] == '-' ||
831 1.2.4.2 perseant (ctx->poic_cp[0] >= '0' && ctx->poic_cp[0] <= '9')) {
832 1.2.4.2 perseant intern = _prop_number_internalize;
833 1.2.4.2 perseant }
834 1.2.4.2 perseant break;
835 1.2.4.2 perseant }
836 1.2.4.2 perseant
837 1.2.4.2 perseant if (intern == NULL) {
838 1.2.4.2 perseant while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
839 1.2.4.2 perseant iter_func = (prop_object_internalizer_continue_t)iter;
840 1.2.4.2 perseant (*iter_func)(&stack, &obj, ctx, data, NULL);
841 1.2.4.2 perseant }
842 1.2.4.2 perseant return NULL;
843 1.2.4.2 perseant }
844 1.2.4.2 perseant
845 1.2.4.2 perseant obj = NULL;
846 1.2.4.2 perseant if ((*intern)(&stack, &obj, ctx) == false) {
847 1.2.4.2 perseant goto match_start;
848 1.2.4.2 perseant }
849 1.2.4.2 perseant
850 1.2.4.2 perseant parent_obj = obj;
851 1.2.4.2 perseant while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
852 1.2.4.2 perseant iter_func = (prop_object_internalizer_continue_t)iter;
853 1.2.4.2 perseant if ((*iter_func)(&stack, &parent_obj, ctx, data,
854 1.2.4.2 perseant obj) == false) {
855 1.2.4.2 perseant goto match_start;
856 1.2.4.2 perseant }
857 1.2.4.2 perseant obj = parent_obj;
858 1.2.4.2 perseant }
859 1.2.4.2 perseant
860 1.2.4.2 perseant /* Ensure there's no trailing junk. */
861 1.2.4.2 perseant if (parent_obj != NULL) {
862 1.2.4.2 perseant ctx->poic_cp = _prop_intern_skip_whitespace(ctx->poic_cp);
863 1.2.4.2 perseant if (!_PROP_EOF(*ctx->poic_cp)) {
864 1.2.4.2 perseant prop_object_release(parent_obj);
865 1.2.4.2 perseant parent_obj = NULL;
866 1.2.4.2 perseant }
867 1.2.4.2 perseant }
868 1.2.4.2 perseant return parent_obj;
869 1.2.4.2 perseant }
870 1.2.4.2 perseant
871 1.2.4.2 perseant /*
872 1.2.4.2 perseant * _prop_object_internalize_xml --
873 1.2.4.2 perseant * Internalize a property list from XML data.
874 1.2.4.2 perseant */
875 1.2.4.2 perseant static prop_object_t
876 1.2.4.2 perseant _prop_object_internalize_xml(struct _prop_object_internalize_context *ctx,
877 1.2.4.2 perseant const struct _prop_object_type_tags *initial_tag)
878 1.2.4.2 perseant {
879 1.2.4.2 perseant prop_object_t obj = NULL;
880 1.2.4.2 perseant
881 1.2.4.2 perseant /* We start with a <plist> tag. */
882 1.2.4.2 perseant if (_prop_xml_intern_find_tag(ctx, "plist",
883 1.2.4.2 perseant _PROP_TAG_TYPE_START) == false) {
884 1.2.4.2 perseant goto out;
885 1.2.4.2 perseant }
886 1.2.4.2 perseant
887 1.2.4.2 perseant /* Plist elements cannot be empty. */
888 1.2.4.2 perseant if (ctx->poic_is_empty_element) {
889 1.2.4.2 perseant goto out;
890 1.2.4.2 perseant }
891 1.2.4.2 perseant
892 1.2.4.2 perseant /*
893 1.2.4.2 perseant * We don't understand any plist attributes, but Apple XML
894 1.2.4.2 perseant * property lists often have a "version" attribute. If we
895 1.2.4.2 perseant * see that one, we simply ignore it.
896 1.2.4.2 perseant */
897 1.2.4.2 perseant if (ctx->poic_tagattr != NULL &&
898 1.2.4.2 perseant !_PROP_TAGATTR_MATCH(ctx, "version")) {
899 1.2.4.2 perseant goto out;
900 1.2.4.2 perseant }
901 1.2.4.2 perseant
902 1.2.4.2 perseant /* Next we expect to see opening main tag. */
903 1.2.4.2 perseant if (_prop_xml_intern_find_tag(ctx,
904 1.2.4.2 perseant initial_tag != NULL ? initial_tag->xml_tag
905 1.2.4.2 perseant : NULL,
906 1.2.4.2 perseant _PROP_TAG_TYPE_START) == false) {
907 1.2.4.2 perseant goto out;
908 1.2.4.2 perseant }
909 1.2.4.2 perseant
910 1.2.4.2 perseant obj = _prop_xml_intern_by_tag(ctx);
911 1.2.4.2 perseant if (obj == NULL) {
912 1.2.4.2 perseant goto out;
913 1.2.4.2 perseant }
914 1.2.4.2 perseant
915 1.2.4.2 perseant /*
916 1.2.4.2 perseant * We've advanced past the closing main tag.
917 1.2.4.2 perseant * Now we want </plist>.
918 1.2.4.2 perseant */
919 1.2.4.2 perseant if (_prop_xml_intern_find_tag(ctx, "plist",
920 1.2.4.2 perseant _PROP_TAG_TYPE_END) == false) {
921 1.2.4.2 perseant prop_object_release(obj);
922 1.2.4.2 perseant obj = NULL;
923 1.2.4.2 perseant }
924 1.2.4.2 perseant out:
925 1.2.4.2 perseant return obj;
926 1.2.4.2 perseant }
927 1.2.4.2 perseant
928 1.2.4.2 perseant /*
929 1.2.4.2 perseant * _prop_object_internalize --
930 1.2.4.2 perseant * Internalize a property list from a NUL-terminated data blob.
931 1.2.4.2 perseant */
932 1.2.4.2 perseant prop_object_t
933 1.2.4.2 perseant _prop_object_internalize(const char *data,
934 1.2.4.2 perseant const struct _prop_object_type_tags *initial_tag)
935 1.2.4.2 perseant {
936 1.2.4.2 perseant struct _prop_object_internalize_context *ctx;
937 1.2.4.2 perseant prop_object_t obj;
938 1.2.4.2 perseant prop_format_t fmt;
939 1.2.4.2 perseant
940 1.2.4.2 perseant /*
941 1.2.4.2 perseant * Skip all whitespace until and look at the first
942 1.2.4.2 perseant * non-whitespace character to determine the format:
943 1.2.4.2 perseant * An XML plist will always have '<' as the first non-ws
944 1.2.4.2 perseant * character. If we encounter something else, we assume
945 1.2.4.2 perseant * it is JSON.
946 1.2.4.2 perseant */
947 1.2.4.2 perseant data = _prop_intern_skip_whitespace(data);
948 1.2.4.2 perseant if (_PROP_EOF(*data)) {
949 1.2.4.2 perseant return NULL;
950 1.2.4.2 perseant }
951 1.2.4.2 perseant
952 1.2.4.2 perseant fmt = *data == '<' ? PROP_FORMAT_XML : PROP_FORMAT_JSON;
953 1.2.4.2 perseant
954 1.2.4.2 perseant ctx = _prop_intern_context_alloc(data, fmt);
955 1.2.4.2 perseant if (ctx == NULL) {
956 1.2.4.2 perseant return NULL;
957 1.2.4.2 perseant }
958 1.2.4.2 perseant
959 1.2.4.2 perseant switch (fmt) {
960 1.2.4.2 perseant case PROP_FORMAT_JSON:
961 1.2.4.2 perseant obj = _prop_object_internalize_json(ctx, initial_tag);
962 1.2.4.2 perseant break;
963 1.2.4.2 perseant
964 1.2.4.2 perseant default: /* PROP_FORMAT_XML */
965 1.2.4.2 perseant obj = _prop_object_internalize_xml(ctx, initial_tag);
966 1.2.4.2 perseant break;
967 1.2.4.2 perseant }
968 1.2.4.2 perseant
969 1.2.4.2 perseant _prop_intern_context_free(ctx);
970 1.2.4.2 perseant return obj;
971 1.2.4.2 perseant }
972 1.2.4.2 perseant
973 1.2.4.2 perseant _PROP_EXPORT prop_object_t
974 1.2.4.2 perseant prop_object_internalize(const char *data)
975 1.2.4.2 perseant {
976 1.2.4.2 perseant return _prop_object_internalize(data, NULL);
977 1.2.4.2 perseant }
978 1.2.4.2 perseant
979 1.2.4.2 perseant #if !defined(_KERNEL) && !defined(_STANDALONE)
980 1.2.4.2 perseant struct _prop_intern_mapped_file {
981 1.2.4.2 perseant char * pimf_data;
982 1.2.4.2 perseant size_t pimf_mapsize;
983 1.2.4.2 perseant };
984 1.2.4.2 perseant
985 1.2.4.2 perseant /*
986 1.2.4.2 perseant * _prop_intern_map_file --
987 1.2.4.2 perseant * Map a file for the purpose of internalizing it.
988 1.2.4.2 perseant */
989 1.2.4.2 perseant static struct _prop_intern_mapped_file *
990 1.2.4.2 perseant _prop_intern_map_file(const char *fname)
991 1.2.4.2 perseant {
992 1.2.4.2 perseant struct stat sb;
993 1.2.4.2 perseant struct _prop_intern_mapped_file *mf;
994 1.2.4.2 perseant size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
995 1.2.4.2 perseant size_t pgmask = pgsize - 1;
996 1.2.4.2 perseant int fd;
997 1.2.4.2 perseant
998 1.2.4.2 perseant mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
999 1.2.4.2 perseant if (mf == NULL) {
1000 1.2.4.2 perseant return NULL;
1001 1.2.4.2 perseant }
1002 1.2.4.2 perseant
1003 1.2.4.2 perseant fd = open(fname, O_RDONLY, 0400);
1004 1.2.4.2 perseant if (fd == -1) {
1005 1.2.4.2 perseant _PROP_FREE(mf, M_TEMP);
1006 1.2.4.2 perseant return NULL;
1007 1.2.4.2 perseant }
1008 1.2.4.2 perseant
1009 1.2.4.2 perseant if (fstat(fd, &sb) == -1) {
1010 1.2.4.2 perseant (void) close(fd);
1011 1.2.4.2 perseant _PROP_FREE(mf, M_TEMP);
1012 1.2.4.2 perseant return NULL;
1013 1.2.4.2 perseant }
1014 1.2.4.2 perseant mf->pimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
1015 1.2.4.2 perseant if (mf->pimf_mapsize < (size_t)sb.st_size) {
1016 1.2.4.2 perseant (void) close(fd);
1017 1.2.4.2 perseant _PROP_FREE(mf, M_TEMP);
1018 1.2.4.2 perseant return NULL;
1019 1.2.4.2 perseant }
1020 1.2.4.2 perseant
1021 1.2.4.2 perseant /*
1022 1.2.4.2 perseant * If the file length is an integral number of pages, then we
1023 1.2.4.2 perseant * need to map a guard page at the end in order to provide the
1024 1.2.4.2 perseant * necessary NUL-termination of the buffer.
1025 1.2.4.2 perseant */
1026 1.2.4.2 perseant bool need_guard = (sb.st_size & pgmask) == 0;
1027 1.2.4.2 perseant
1028 1.2.4.2 perseant mf->pimf_data = mmap(NULL, need_guard ? mf->pimf_mapsize + pgsize
1029 1.2.4.2 perseant : mf->pimf_mapsize,
1030 1.2.4.2 perseant PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
1031 1.2.4.2 perseant (void) close(fd);
1032 1.2.4.2 perseant if (mf->pimf_data == MAP_FAILED) {
1033 1.2.4.2 perseant _PROP_FREE(mf, M_TEMP);
1034 1.2.4.2 perseant return (NULL);
1035 1.2.4.2 perseant }
1036 1.2.4.2 perseant #ifdef POSIX_MADV_SEQUENTIAL
1037 1.2.4.2 perseant (void) posix_madvise(mf->pimf_data, mf->pimf_mapsize,
1038 1.2.4.2 perseant POSIX_MADV_SEQUENTIAL);
1039 1.2.4.2 perseant #endif
1040 1.2.4.2 perseant
1041 1.2.4.2 perseant if (need_guard) {
1042 1.2.4.2 perseant if (mmap(mf->pimf_data + mf->pimf_mapsize,
1043 1.2.4.2 perseant pgsize, PROT_READ,
1044 1.2.4.2 perseant MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
1045 1.2.4.2 perseant (off_t)0) == MAP_FAILED) {
1046 1.2.4.2 perseant (void) munmap(mf->pimf_data, mf->pimf_mapsize);
1047 1.2.4.2 perseant _PROP_FREE(mf, M_TEMP);
1048 1.2.4.2 perseant return NULL;
1049 1.2.4.2 perseant }
1050 1.2.4.2 perseant mf->pimf_mapsize += pgsize;
1051 1.2.4.2 perseant }
1052 1.2.4.2 perseant return mf;
1053 1.2.4.2 perseant }
1054 1.2.4.2 perseant
1055 1.2.4.2 perseant /*
1056 1.2.4.2 perseant * _prop_intern_unmap_file --
1057 1.2.4.2 perseant * Unmap a file previously mapped for internalizing.
1058 1.2.4.2 perseant */
1059 1.2.4.2 perseant static void
1060 1.2.4.2 perseant _prop_intern_unmap_file(struct _prop_intern_mapped_file *mf)
1061 1.2.4.2 perseant {
1062 1.2.4.2 perseant #ifdef POSIX_MADV_DONTNEED
1063 1.2.4.2 perseant (void) posix_madvise(mf->pimf_data, mf->pimf_mapsize,
1064 1.2.4.2 perseant POSIX_MADV_DONTNEED);
1065 1.2.4.2 perseant #endif
1066 1.2.4.2 perseant (void) munmap(mf->pimf_data, mf->pimf_mapsize);
1067 1.2.4.2 perseant _PROP_FREE(mf, M_TEMP);
1068 1.2.4.2 perseant }
1069 1.2.4.2 perseant
1070 1.2.4.2 perseant /*
1071 1.2.4.2 perseant * _prop_object_internalize_from_file --
1072 1.2.4.2 perseant * Internalize a property list from a file.
1073 1.2.4.2 perseant */
1074 1.2.4.2 perseant prop_object_t
1075 1.2.4.2 perseant _prop_object_internalize_from_file(const char *fname,
1076 1.2.4.2 perseant const struct _prop_object_type_tags *initial_tag)
1077 1.2.4.2 perseant {
1078 1.2.4.2 perseant struct _prop_intern_mapped_file *mf;
1079 1.2.4.2 perseant prop_object_t obj;
1080 1.2.4.2 perseant
1081 1.2.4.2 perseant mf = _prop_intern_map_file(fname);
1082 1.2.4.2 perseant if (mf == NULL) {
1083 1.2.4.2 perseant return NULL;
1084 1.2.4.2 perseant }
1085 1.2.4.2 perseant obj = _prop_object_internalize(mf->pimf_data, initial_tag);
1086 1.2.4.2 perseant _prop_intern_unmap_file(mf);
1087 1.2.4.2 perseant
1088 1.2.4.2 perseant return obj;
1089 1.2.4.2 perseant }
1090 1.2.4.2 perseant
1091 1.2.4.2 perseant _PROP_EXPORT prop_object_t
1092 1.2.4.2 perseant prop_object_internalize_from_file(const char *fname)
1093 1.2.4.2 perseant {
1094 1.2.4.2 perseant return _prop_object_internalize_from_file(fname, NULL);
1095 1.2.4.2 perseant }
1096 1.2.4.2 perseant #endif /* !_KERNEL && !_STANDALONE */
1097