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