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