1 1.2 thorpej /* $NetBSD: prop_extern.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/stat.h> 37 1.1 thorpej #include <errno.h> 38 1.1 thorpej #include <fcntl.h> 39 1.1 thorpej #include <limits.h> 40 1.1 thorpej #include <unistd.h> 41 1.1 thorpej #endif /* !_KERNEL && !_STANDALONE */ 42 1.1 thorpej 43 1.1 thorpej #define BUF_EXPAND 256 44 1.1 thorpej #define PLISTTMP "/.plistXXXXXX" 45 1.1 thorpej 46 1.1 thorpej static prop_format_t _prop_format_default = PROP_FORMAT_XML; 47 1.1 thorpej 48 1.1 thorpej /* 49 1.1 thorpej * _prop_extern_append_char -- 50 1.1 thorpej * Append a single character to the externalize buffer. 51 1.1 thorpej */ 52 1.1 thorpej bool 53 1.1 thorpej _prop_extern_append_char( 54 1.1 thorpej struct _prop_object_externalize_context *ctx, unsigned char c) 55 1.1 thorpej { 56 1.1 thorpej 57 1.1 thorpej _PROP_ASSERT(ctx->poec_capacity != 0); 58 1.1 thorpej _PROP_ASSERT(ctx->poec_buf != NULL); 59 1.1 thorpej _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); 60 1.1 thorpej 61 1.1 thorpej if (ctx->poec_len == ctx->poec_capacity) { 62 1.1 thorpej char *cp = _PROP_REALLOC(ctx->poec_buf, 63 1.1 thorpej ctx->poec_capacity + BUF_EXPAND, 64 1.1 thorpej M_TEMP); 65 1.1 thorpej if (cp == NULL) { 66 1.1 thorpej return false; 67 1.1 thorpej } 68 1.1 thorpej ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND; 69 1.1 thorpej ctx->poec_buf = cp; 70 1.1 thorpej } 71 1.1 thorpej 72 1.1 thorpej ctx->poec_buf[ctx->poec_len++] = c; 73 1.1 thorpej 74 1.1 thorpej return true; 75 1.1 thorpej } 76 1.1 thorpej 77 1.1 thorpej /* 78 1.1 thorpej * _prop_extern_append_cstring -- 79 1.1 thorpej * Append a C string to the externalize buffer. 80 1.1 thorpej */ 81 1.1 thorpej bool 82 1.1 thorpej _prop_extern_append_cstring( 83 1.1 thorpej struct _prop_object_externalize_context *ctx, const char *cp) 84 1.1 thorpej { 85 1.1 thorpej 86 1.1 thorpej while (*cp != '\0') { 87 1.1 thorpej if (_prop_extern_append_char(ctx, 88 1.1 thorpej (unsigned char)*cp) == false) { 89 1.1 thorpej return false; 90 1.1 thorpej } 91 1.1 thorpej cp++; 92 1.1 thorpej } 93 1.1 thorpej return true; 94 1.1 thorpej } 95 1.1 thorpej 96 1.1 thorpej /* 97 1.1 thorpej * _prop_json_extern_append_encoded_cstring -- 98 1.1 thorpej * Append a C string to the externalize buffer, JSON-encoded. 99 1.1 thorpej */ 100 1.1 thorpej static bool 101 1.1 thorpej _prop_json_extern_append_escu( 102 1.1 thorpej struct _prop_object_externalize_context *ctx, uint16_t val) 103 1.1 thorpej { 104 1.1 thorpej char tmpstr[sizeof("\\uXXXX")]; 105 1.1 thorpej 106 1.1 thorpej snprintf(tmpstr, sizeof(tmpstr), "\\u%04X", val); 107 1.1 thorpej return _prop_extern_append_cstring(ctx, tmpstr); 108 1.1 thorpej } 109 1.1 thorpej 110 1.1 thorpej static bool 111 1.1 thorpej _prop_json_extern_append_encoded_cstring( 112 1.1 thorpej struct _prop_object_externalize_context *ctx, const char *cp) 113 1.1 thorpej { 114 1.1 thorpej bool esc; 115 1.1 thorpej unsigned char ch; 116 1.1 thorpej 117 1.1 thorpej for (; (ch = *cp) != '\0'; cp++) { 118 1.1 thorpej esc = true; 119 1.1 thorpej switch (ch) { 120 1.1 thorpej /* 121 1.1 thorpej * First, the two explicit exclusions. They must be 122 1.1 thorpej * escaped. 123 1.1 thorpej */ 124 1.1 thorpej case '"': /* U+0022 quotation mark */ 125 1.1 thorpej goto emit; 126 1.1 thorpej 127 1.1 thorpej case '\\': /* U+005C reverse solidus */ 128 1.1 thorpej goto emit; 129 1.1 thorpej 130 1.1 thorpej /* 131 1.1 thorpej * And some special cases that are explcit in the grammar. 132 1.1 thorpej */ 133 1.1 thorpej case '/': /* U+002F solidus (XXX this one seems silly) */ 134 1.1 thorpej goto emit; 135 1.1 thorpej 136 1.1 thorpej case 0x08: /* U+0008 backspace */ 137 1.1 thorpej ch = 'b'; 138 1.1 thorpej goto emit; 139 1.1 thorpej 140 1.1 thorpej case 0x0c: /* U+000C form feed */ 141 1.1 thorpej ch = 'f'; 142 1.1 thorpej goto emit; 143 1.1 thorpej 144 1.1 thorpej case 0x0a: /* U+000A line feed */ 145 1.1 thorpej ch = 'n'; 146 1.1 thorpej goto emit; 147 1.1 thorpej 148 1.1 thorpej case 0x0d: /* U+000D carriage return */ 149 1.1 thorpej ch = 'r'; 150 1.1 thorpej goto emit; 151 1.1 thorpej 152 1.1 thorpej case 0x09: /* U+0009 tab */ 153 1.1 thorpej ch = 't'; 154 1.1 thorpej goto emit; 155 1.1 thorpej 156 1.1 thorpej default: 157 1.1 thorpej /* 158 1.1 thorpej * \u-escape all other single-byte ASCII control 159 1.1 thorpej * characters, per RFC 8259: 160 1.1 thorpej * 161 1.1 thorpej * <quote> 162 1.1 thorpej * All Unicode characters may be placed within the 163 1.1 thorpej * quotation marks, except for the characters that 164 1.1 thorpej * MUST be escaped: quotation mark, reverse solidus, 165 1.1 thorpej * and the control characters (U+0000 through U+001F). 166 1.1 thorpej * </quote> 167 1.1 thorpej */ 168 1.1 thorpej if (ch < 0x20) { 169 1.1 thorpej if (_prop_json_extern_append_escu(ctx, 170 1.1 thorpej ch) == false) { 171 1.1 thorpej return false; 172 1.1 thorpej } 173 1.1 thorpej break; 174 1.1 thorpej } 175 1.1 thorpej /* 176 1.1 thorpej * We're going to just treat everything else like 177 1.1 thorpej * UTF-8 (we've been handed a C-string, after all) 178 1.1 thorpej * and pretend it will be OK. 179 1.1 thorpej */ 180 1.1 thorpej esc = false; 181 1.1 thorpej emit: 182 1.1 thorpej if ((esc && _prop_extern_append_char(ctx, 183 1.1 thorpej '\\') == false) || 184 1.1 thorpej _prop_extern_append_char(ctx, ch) == false) { 185 1.1 thorpej return false; 186 1.1 thorpej } 187 1.1 thorpej break; 188 1.1 thorpej } 189 1.1 thorpej } 190 1.1 thorpej 191 1.1 thorpej return true; 192 1.1 thorpej } 193 1.1 thorpej 194 1.1 thorpej /* 195 1.1 thorpej * _prop_xml_extern_append_encoded_cstring -- 196 1.1 thorpej * Append a C string to the externalize buffer, XML-encoded. 197 1.1 thorpej */ 198 1.1 thorpej static bool 199 1.1 thorpej _prop_xml_extern_append_encoded_cstring( 200 1.1 thorpej struct _prop_object_externalize_context *ctx, const char *cp) 201 1.1 thorpej { 202 1.1 thorpej bool rv; 203 1.1 thorpej unsigned char ch; 204 1.1 thorpej 205 1.1 thorpej for (rv = true; rv && (ch = *cp) != '\0'; cp++) { 206 1.1 thorpej switch (ch) { 207 1.1 thorpej case '<': 208 1.1 thorpej rv = _prop_extern_append_cstring(ctx, "<"); 209 1.1 thorpej break; 210 1.1 thorpej case '>': 211 1.1 thorpej rv = _prop_extern_append_cstring(ctx, ">"); 212 1.1 thorpej break; 213 1.1 thorpej case '&': 214 1.1 thorpej rv = _prop_extern_append_cstring(ctx, "&"); 215 1.1 thorpej break; 216 1.1 thorpej default: 217 1.1 thorpej rv = _prop_extern_append_char(ctx, ch); 218 1.1 thorpej break; 219 1.1 thorpej } 220 1.1 thorpej } 221 1.1 thorpej 222 1.1 thorpej return rv; 223 1.1 thorpej } 224 1.1 thorpej 225 1.1 thorpej /* 226 1.1 thorpej * _prop_extern_append_encoded_cstring -- 227 1.1 thorpej * Append a C string to the externalize buffer, encoding it for 228 1.1 thorpej * the selected format. 229 1.1 thorpej */ 230 1.1 thorpej bool 231 1.1 thorpej _prop_extern_append_encoded_cstring( 232 1.1 thorpej struct _prop_object_externalize_context *ctx, const char *cp) 233 1.1 thorpej { 234 1.1 thorpej _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || 235 1.1 thorpej ctx->poec_format == PROP_FORMAT_JSON); 236 1.1 thorpej 237 1.1 thorpej switch (ctx->poec_format) { 238 1.1 thorpej case PROP_FORMAT_JSON: 239 1.1 thorpej return _prop_json_extern_append_encoded_cstring(ctx, cp); 240 1.1 thorpej 241 1.1 thorpej default: /* PROP_FORMAT_XML */ 242 1.1 thorpej return _prop_xml_extern_append_encoded_cstring(ctx, cp); 243 1.1 thorpej } 244 1.1 thorpej } 245 1.1 thorpej 246 1.1 thorpej /* 247 1.1 thorpej * _prop_extern_start_line -- 248 1.1 thorpej * Append the start-of-line character sequence. 249 1.1 thorpej */ 250 1.1 thorpej bool 251 1.1 thorpej _prop_extern_start_line( 252 1.1 thorpej struct _prop_object_externalize_context *ctx) 253 1.1 thorpej { 254 1.1 thorpej unsigned int i; 255 1.1 thorpej 256 1.1 thorpej for (i = 0; i < ctx->poec_depth; i++) { 257 1.1 thorpej if (_prop_extern_append_char(ctx, '\t') == false) { 258 1.1 thorpej return false; 259 1.1 thorpej } 260 1.1 thorpej } 261 1.1 thorpej return true; 262 1.1 thorpej } 263 1.1 thorpej 264 1.1 thorpej /* 265 1.1 thorpej * _prop_extern_end_line -- 266 1.1 thorpej * Append the end-of-line character sequence. 267 1.1 thorpej */ 268 1.1 thorpej bool 269 1.1 thorpej _prop_extern_end_line( 270 1.1 thorpej struct _prop_object_externalize_context *ctx, const char *trailer) 271 1.1 thorpej { 272 1.1 thorpej if (trailer != NULL && 273 1.1 thorpej _prop_extern_append_cstring(ctx, trailer) == false) { 274 1.1 thorpej return false; 275 1.1 thorpej } 276 1.1 thorpej return _prop_extern_append_char(ctx, '\n'); 277 1.1 thorpej } 278 1.1 thorpej 279 1.1 thorpej /* 280 1.1 thorpej * _prop_extern_append_start_tag -- 281 1.1 thorpej * Append an item's start tag to the externalize buffer. 282 1.1 thorpej */ 283 1.1 thorpej bool 284 1.1 thorpej _prop_extern_append_start_tag( 285 1.1 thorpej struct _prop_object_externalize_context *ctx, 286 1.1 thorpej const struct _prop_object_type_tags *tags, 287 1.1 thorpej const char *tagattrs) 288 1.1 thorpej { 289 1.1 thorpej bool rv; 290 1.1 thorpej 291 1.1 thorpej _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || 292 1.1 thorpej ctx->poec_format == PROP_FORMAT_JSON); 293 1.1 thorpej 294 1.1 thorpej switch (ctx->poec_format) { 295 1.1 thorpej case PROP_FORMAT_JSON: 296 1.1 thorpej rv = tags->json_open_tag == NULL || 297 1.1 thorpej _prop_extern_append_cstring(ctx, tags->json_open_tag); 298 1.1 thorpej break; 299 1.1 thorpej 300 1.1 thorpej default: /* PROP_FORMAT_XML */ 301 1.1 thorpej rv = _prop_extern_append_char(ctx, '<') && 302 1.1 thorpej _prop_extern_append_cstring(ctx, tags->xml_tag) && 303 1.1 thorpej (tagattrs == NULL || 304 1.1 thorpej (_prop_extern_append_char(ctx, ' ') && 305 1.1 thorpej _prop_extern_append_cstring(ctx, tagattrs))) && 306 1.1 thorpej _prop_extern_append_char(ctx, '>'); 307 1.1 thorpej break; 308 1.1 thorpej } 309 1.1 thorpej 310 1.1 thorpej return rv; 311 1.1 thorpej } 312 1.1 thorpej 313 1.1 thorpej /* 314 1.1 thorpej * _prop_extern_append_end_tag -- 315 1.1 thorpej * Append an item's end tag to the externalize buffer. 316 1.1 thorpej */ 317 1.1 thorpej bool 318 1.1 thorpej _prop_extern_append_end_tag( 319 1.1 thorpej struct _prop_object_externalize_context *ctx, 320 1.1 thorpej const struct _prop_object_type_tags *tags) 321 1.1 thorpej { 322 1.1 thorpej bool rv; 323 1.1 thorpej 324 1.1 thorpej _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || 325 1.1 thorpej ctx->poec_format == PROP_FORMAT_JSON); 326 1.1 thorpej 327 1.1 thorpej switch (ctx->poec_format) { 328 1.1 thorpej case PROP_FORMAT_JSON: 329 1.1 thorpej rv = tags->json_close_tag == NULL || 330 1.1 thorpej _prop_extern_append_cstring(ctx, tags->json_close_tag); 331 1.1 thorpej break; 332 1.1 thorpej 333 1.1 thorpej default: /* PROP_FORMAT_XML */ 334 1.1 thorpej rv = _prop_extern_append_char(ctx, '<') && 335 1.1 thorpej _prop_extern_append_char(ctx, '/') && 336 1.1 thorpej _prop_extern_append_cstring(ctx, tags->xml_tag) && 337 1.1 thorpej _prop_extern_append_char(ctx, '>'); 338 1.1 thorpej break; 339 1.1 thorpej } 340 1.1 thorpej 341 1.1 thorpej return rv; 342 1.1 thorpej } 343 1.1 thorpej 344 1.1 thorpej /* 345 1.1 thorpej * _prop_extern_append_empty_tag -- 346 1.1 thorpej * Append an item's empty tag to the externalize buffer. 347 1.1 thorpej */ 348 1.1 thorpej bool 349 1.1 thorpej _prop_extern_append_empty_tag( 350 1.1 thorpej struct _prop_object_externalize_context *ctx, 351 1.1 thorpej const struct _prop_object_type_tags *tags) 352 1.1 thorpej { 353 1.1 thorpej bool rv; 354 1.1 thorpej 355 1.1 thorpej _PROP_ASSERT(ctx->poec_format == PROP_FORMAT_XML || 356 1.1 thorpej ctx->poec_format == PROP_FORMAT_JSON); 357 1.1 thorpej 358 1.1 thorpej switch (ctx->poec_format) { 359 1.1 thorpej case PROP_FORMAT_JSON: 360 1.1 thorpej if (tags->json_open_tag == NULL || 361 1.1 thorpej _prop_extern_append_cstring(ctx, 362 1.1 thorpej tags->json_open_tag) == false) { 363 1.1 thorpej return false; 364 1.1 thorpej } 365 1.1 thorpej if (tags->json_empty_sep != NULL && 366 1.1 thorpej _prop_extern_append_cstring(ctx, 367 1.1 thorpej tags->json_empty_sep) == false) { 368 1.1 thorpej return false; 369 1.1 thorpej } 370 1.1 thorpej if (tags->json_close_tag != NULL) { 371 1.1 thorpej rv = _prop_extern_append_cstring(ctx, 372 1.1 thorpej tags->json_close_tag); 373 1.1 thorpej } else { 374 1.1 thorpej rv = true; 375 1.1 thorpej } 376 1.1 thorpej break; 377 1.1 thorpej 378 1.1 thorpej default: /* PROP_FORMAT_XML */ 379 1.1 thorpej rv = _prop_extern_append_char(ctx, '<') && 380 1.1 thorpej _prop_extern_append_cstring(ctx, tags->xml_tag) && 381 1.1 thorpej _prop_extern_append_char(ctx, '/') && 382 1.1 thorpej _prop_extern_append_char(ctx, '>'); 383 1.1 thorpej break; 384 1.1 thorpej } 385 1.1 thorpej 386 1.1 thorpej return rv; 387 1.1 thorpej } 388 1.1 thorpej 389 1.1 thorpej static const struct _prop_object_type_tags _plist_type_tags = { 390 1.1 thorpej .xml_tag = "plist", 391 1.1 thorpej }; 392 1.1 thorpej 393 1.1 thorpej /* 394 1.1 thorpej * _prop_extern_append_header -- 395 1.1 thorpej * Append the header to the externalize buffer. 396 1.1 thorpej */ 397 1.1 thorpej static bool 398 1.1 thorpej _prop_extern_append_header(struct _prop_object_externalize_context *ctx) 399 1.1 thorpej { 400 1.1 thorpej static const char _plist_xml_header[] = 401 1.1 thorpej "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 402 1.1 thorpej "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"; 403 1.1 thorpej 404 1.1 thorpej if (ctx->poec_format != PROP_FORMAT_XML) { 405 1.1 thorpej return true; 406 1.1 thorpej } 407 1.1 thorpej 408 1.1 thorpej if (_prop_extern_append_cstring(ctx, _plist_xml_header) == false || 409 1.1 thorpej _prop_extern_append_start_tag(ctx, 410 1.1 thorpej &_plist_type_tags, 411 1.1 thorpej "version=\"1.0\"") == false || 412 1.1 thorpej _prop_extern_append_char(ctx, '\n') == false) { 413 1.1 thorpej return false; 414 1.1 thorpej } 415 1.1 thorpej 416 1.1 thorpej return true; 417 1.1 thorpej } 418 1.1 thorpej 419 1.1 thorpej /* 420 1.1 thorpej * _prop_extern_append_footer -- 421 1.1 thorpej * Append the footer to the externalize buffer. This also 422 1.1 thorpej * NUL-terminates the buffer. 423 1.1 thorpej */ 424 1.1 thorpej static bool 425 1.1 thorpej _prop_extern_append_footer(struct _prop_object_externalize_context *ctx) 426 1.1 thorpej { 427 1.1 thorpej if (_prop_extern_end_line(ctx, NULL) == false) { 428 1.1 thorpej return false; 429 1.1 thorpej } 430 1.1 thorpej 431 1.1 thorpej if (ctx->poec_format == PROP_FORMAT_XML) { 432 1.1 thorpej if (_prop_extern_append_end_tag(ctx, 433 1.1 thorpej &_plist_type_tags) == false || 434 1.1 thorpej _prop_extern_end_line(ctx, NULL) == false) { 435 1.1 thorpej return false; 436 1.1 thorpej } 437 1.1 thorpej } 438 1.1 thorpej 439 1.1 thorpej return _prop_extern_append_char(ctx, '\0'); 440 1.1 thorpej } 441 1.1 thorpej 442 1.1 thorpej /* 443 1.1 thorpej * _prop_extern_context_alloc -- 444 1.1 thorpej * Allocate an externalize context. 445 1.1 thorpej */ 446 1.1 thorpej static struct _prop_object_externalize_context * 447 1.1 thorpej _prop_extern_context_alloc(prop_format_t fmt) 448 1.1 thorpej { 449 1.1 thorpej struct _prop_object_externalize_context *ctx; 450 1.1 thorpej 451 1.1 thorpej ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); 452 1.1 thorpej if (ctx != NULL) { 453 1.1 thorpej ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP); 454 1.1 thorpej if (ctx->poec_buf == NULL) { 455 1.1 thorpej _PROP_FREE(ctx, M_TEMP); 456 1.1 thorpej return NULL; 457 1.1 thorpej } 458 1.1 thorpej ctx->poec_len = 0; 459 1.1 thorpej ctx->poec_capacity = BUF_EXPAND; 460 1.1 thorpej ctx->poec_depth = 0; 461 1.1 thorpej ctx->poec_format = fmt; 462 1.1 thorpej } 463 1.1 thorpej return ctx; 464 1.1 thorpej } 465 1.1 thorpej 466 1.1 thorpej /* 467 1.1 thorpej * _prop_extern_context_free -- 468 1.1 thorpej * Free an externalize context. 469 1.1 thorpej */ 470 1.1 thorpej static void 471 1.1 thorpej _prop_extern_context_free(struct _prop_object_externalize_context *ctx) 472 1.1 thorpej { 473 1.1 thorpej /* Buffer is always freed by the caller. */ 474 1.1 thorpej _PROP_FREE(ctx, M_TEMP); 475 1.1 thorpej } 476 1.1 thorpej 477 1.1 thorpej /* 478 1.1 thorpej * _prop_object_externalize -- 479 1.1 thorpej * Externalize an object, returning a NUL-terminated buffer 480 1.1 thorpej * containing the serialized data in either XML or JSON format. 481 1.1 thorpej * The buffer is allocated with the M_TEMP memory type. 482 1.1 thorpej */ 483 1.1 thorpej char * 484 1.1 thorpej _prop_object_externalize(struct _prop_object *obj, prop_format_t fmt) 485 1.1 thorpej { 486 1.1 thorpej struct _prop_object_externalize_context *ctx; 487 1.1 thorpej char *cp = NULL; 488 1.1 thorpej 489 1.1 thorpej if (obj == NULL || obj->po_type->pot_extern == NULL) { 490 1.1 thorpej return NULL; 491 1.1 thorpej } 492 1.1 thorpej if (fmt != PROP_FORMAT_XML && fmt != PROP_FORMAT_JSON) { 493 1.1 thorpej return NULL; 494 1.1 thorpej } 495 1.1 thorpej 496 1.1 thorpej ctx = _prop_extern_context_alloc(fmt); 497 1.1 thorpej if (ctx == NULL) { 498 1.1 thorpej return NULL; 499 1.1 thorpej } 500 1.1 thorpej 501 1.1 thorpej if (_prop_extern_append_header(ctx) == false || 502 1.1 thorpej obj->po_type->pot_extern(ctx, obj) == false || 503 1.1 thorpej _prop_extern_append_footer(ctx) == false) { 504 1.1 thorpej /* We are responsible for releasing the buffer. */ 505 1.1 thorpej _PROP_FREE(ctx->poec_buf, M_TEMP); 506 1.1 thorpej goto bad; 507 1.1 thorpej } 508 1.1 thorpej 509 1.1 thorpej cp = ctx->poec_buf; 510 1.1 thorpej bad: 511 1.1 thorpej _prop_extern_context_free(ctx); 512 1.1 thorpej return cp; 513 1.1 thorpej } 514 1.1 thorpej 515 1.1 thorpej #if !defined(_KERNEL) && !defined(_STANDALONE) 516 1.1 thorpej /* 517 1.1 thorpej * _prop_extern_file_dirname -- 518 1.1 thorpej * dirname(3), basically. We have to roll our own because the 519 1.1 thorpej * system dirname(3) isn't reentrant. 520 1.1 thorpej */ 521 1.1 thorpej static void 522 1.1 thorpej _prop_extern_file_dirname(const char *path, char *result) 523 1.1 thorpej { 524 1.1 thorpej const char *lastp; 525 1.1 thorpej size_t len; 526 1.1 thorpej 527 1.1 thorpej /* 528 1.1 thorpej * If `path' is a NULL pointer or points to an empty string, 529 1.1 thorpej * return ".". 530 1.1 thorpej */ 531 1.1 thorpej if (path == NULL || *path == '\0') { 532 1.1 thorpej goto singledot; 533 1.1 thorpej } 534 1.1 thorpej 535 1.1 thorpej /* Strip trailing slashes, if any. */ 536 1.1 thorpej lastp = path + strlen(path) - 1; 537 1.1 thorpej while (lastp != path && *lastp == '/') { 538 1.1 thorpej lastp--; 539 1.1 thorpej } 540 1.1 thorpej 541 1.1 thorpej /* Terminate path at the last occurrence of '/'. */ 542 1.1 thorpej do { 543 1.1 thorpej if (*lastp == '/') { 544 1.1 thorpej /* Strip trailing slashes, if any. */ 545 1.1 thorpej while (lastp != path && *lastp == '/') { 546 1.1 thorpej lastp--; 547 1.1 thorpej } 548 1.1 thorpej 549 1.1 thorpej /* ...and copy the result into the result buffer. */ 550 1.1 thorpej len = (lastp - path) + 1 /* last char */; 551 1.1 thorpej if (len > (PATH_MAX - 1)) { 552 1.1 thorpej len = PATH_MAX - 1; 553 1.1 thorpej } 554 1.1 thorpej 555 1.1 thorpej memcpy(result, path, len); 556 1.1 thorpej result[len] = '\0'; 557 1.1 thorpej return; 558 1.1 thorpej } 559 1.1 thorpej } while (--lastp >= path); 560 1.1 thorpej 561 1.1 thorpej /* No /'s found, return ".". */ 562 1.1 thorpej singledot: 563 1.1 thorpej strcpy(result, "."); 564 1.1 thorpej } 565 1.1 thorpej 566 1.1 thorpej /* 567 1.1 thorpej * _prop_extern_write_file -- 568 1.1 thorpej * Write an externalized object to the specified file. 569 1.1 thorpej * The file is written atomically from the caller's perspective, 570 1.1 thorpej * and the mode set to 0666 modified by the caller's umask. 571 1.1 thorpej */ 572 1.1 thorpej static bool 573 1.1 thorpej _prop_extern_write_file(const char *fname, const char *data, size_t len) 574 1.1 thorpej { 575 1.1 thorpej char tname_store[PATH_MAX]; 576 1.1 thorpej char *tname = NULL; 577 1.1 thorpej int fd = -1; 578 1.1 thorpej int save_errno; 579 1.1 thorpej mode_t myumask; 580 1.1 thorpej bool rv = false; 581 1.1 thorpej 582 1.1 thorpej if (len > SSIZE_MAX) { 583 1.1 thorpej errno = EFBIG; 584 1.1 thorpej return false; 585 1.1 thorpej } 586 1.1 thorpej 587 1.1 thorpej /* 588 1.1 thorpej * Get the directory name where the file is to be written 589 1.1 thorpej * and create the temporary file. 590 1.1 thorpej */ 591 1.1 thorpej _prop_extern_file_dirname(fname, tname_store); 592 1.1 thorpej if (strlen(tname_store) + strlen(PLISTTMP) >= sizeof(tname_store)) { 593 1.1 thorpej errno = ENAMETOOLONG; 594 1.1 thorpej return false; 595 1.1 thorpej } 596 1.1 thorpej strcat(tname_store, PLISTTMP); 597 1.1 thorpej 598 1.1 thorpej if ((fd = mkstemp(tname_store)) == -1) { 599 1.1 thorpej return false; 600 1.1 thorpej } 601 1.1 thorpej tname = tname_store; 602 1.1 thorpej 603 1.1 thorpej if (write(fd, data, len) != (ssize_t)len) { 604 1.1 thorpej goto bad; 605 1.1 thorpej } 606 1.1 thorpej 607 1.1 thorpej if (fsync(fd) == -1) { 608 1.1 thorpej goto bad; 609 1.1 thorpej } 610 1.1 thorpej 611 1.1 thorpej myumask = umask(0); 612 1.1 thorpej (void)umask(myumask); 613 1.1 thorpej if (fchmod(fd, 0666 & ~myumask) == -1) { 614 1.1 thorpej goto bad; 615 1.1 thorpej } 616 1.1 thorpej 617 1.1 thorpej if (rename(tname, fname) == -1) { 618 1.1 thorpej goto bad; 619 1.1 thorpej } 620 1.1 thorpej tname = NULL; 621 1.1 thorpej 622 1.1 thorpej rv = true; 623 1.1 thorpej 624 1.1 thorpej bad: 625 1.1 thorpej save_errno = errno; 626 1.1 thorpej if (fd != -1) { 627 1.1 thorpej (void) close(fd); 628 1.1 thorpej } 629 1.1 thorpej if (tname != NULL) { 630 1.1 thorpej (void) unlink(tname); 631 1.1 thorpej } 632 1.1 thorpej errno = save_errno; 633 1.1 thorpej return rv; 634 1.1 thorpej } 635 1.1 thorpej 636 1.1 thorpej /* 637 1.1 thorpej * _prop_object_externalize_to_file -- 638 1.1 thorpej * Externalize an object to the specified file. 639 1.1 thorpej */ 640 1.1 thorpej bool 641 1.1 thorpej _prop_object_externalize_to_file(struct _prop_object *obj, const char *fname, 642 1.1 thorpej prop_format_t fmt) 643 1.1 thorpej { 644 1.1 thorpej char *data = _prop_object_externalize(obj, fmt); 645 1.1 thorpej if (data == NULL) { 646 1.1 thorpej return false; 647 1.1 thorpej } 648 1.1 thorpej bool rv = _prop_extern_write_file(fname, data, strlen(data)); 649 1.1 thorpej int save_errno = errno; 650 1.1 thorpej _PROP_FREE(data, M_TEMP); 651 1.1 thorpej errno = save_errno; 652 1.1 thorpej 653 1.1 thorpej return rv; 654 1.1 thorpej } 655 1.1 thorpej 656 1.1 thorpej /* 657 1.1 thorpej * prop_object_externalize_to_file -- 658 1.1 thorpej * Externalize an object to the specifed file in the default format. 659 1.1 thorpej */ 660 1.1 thorpej _PROP_EXPORT bool 661 1.1 thorpej prop_object_externalize_to_file(prop_object_t po, const char *fname) 662 1.1 thorpej { 663 1.1 thorpej return _prop_object_externalize_to_file((struct _prop_object *)po, 664 1.1 thorpej fname, _prop_format_default); 665 1.1 thorpej } 666 1.1 thorpej 667 1.1 thorpej /* 668 1.1 thorpej * prop_object_externalize_to_file_with_format -- 669 1.1 thorpej * Externalize an object to the specifed file in the specified format. 670 1.1 thorpej */ 671 1.1 thorpej _PROP_EXPORT bool 672 1.1 thorpej prop_object_externalize_to_file_with_format(prop_object_t po, 673 1.1 thorpej const char *fname, prop_format_t fmt) 674 1.1 thorpej { 675 1.1 thorpej return _prop_object_externalize_to_file((struct _prop_object *)po, 676 1.1 thorpej fname, fmt); 677 1.1 thorpej } 678 1.1 thorpej #endif /* !_KERNEL && !_STANDALONE */ 679 1.1 thorpej 680 1.1 thorpej /* 681 1.1 thorpej * prop_object_externalize -- 682 1.1 thorpej * Externalize an object in the default format. 683 1.1 thorpej */ 684 1.1 thorpej _PROP_EXPORT char * 685 1.1 thorpej prop_object_externalize(prop_object_t po) 686 1.1 thorpej { 687 1.1 thorpej return _prop_object_externalize((struct _prop_object *)po, 688 1.1 thorpej _prop_format_default); 689 1.1 thorpej } 690 1.1 thorpej 691 1.1 thorpej /* 692 1.1 thorpej * prop_object_externalize_with_format -- 693 1.1 thorpej * Externalize an object in the specified format. 694 1.1 thorpej */ 695 1.1 thorpej _PROP_EXPORT char * 696 1.1 thorpej prop_object_externalize_with_format(prop_object_t po, prop_format_t fmt) 697 1.1 thorpej { 698 1.1 thorpej return _prop_object_externalize((struct _prop_object *)po, fmt); 699 1.1 thorpej } 700