diff options
| author | 2026-03-22 19:16:51 +0100 | |
|---|---|---|
| committer | 2026-03-22 19:16:51 +0100 | |
| commit | c8b36a80bad4a1fe488927af4da0ecbcf10079bb (patch) | |
| tree | 657066f92a85dc43bd51af7698e76d00c29c0b4a /src | |
| parent | 3edac28498d883f1f768699ee15ce85a82bb2a7b (diff) | |
| download | libplist-c8b36a80bad4a1fe488927af4da0ecbcf10079bb.tar.gz libplist-c8b36a80bad4a1fe488927af4da0ecbcf10079bb.tar.bz2 | |
Add OpenStep coercion support for non-OpenStep plist types
- Use PLIST_OPT_COERCE option to coerce PLIST_BOOLEAN, PLIST_DATE, PLIST_UID, and PLIST_NULL to OpenStep-compatible types (1 or 0, ISO 8601 strings, integers, and 'NULL' string)
- Add plist_to_openstep_with_options() function to allow passing coercion option (and others)
- Update plist_write_to_string() and plist_write_to_stream() accordingly
Diffstat (limited to 'src')
| -rw-r--r-- | src/oplist.c | 143 | ||||
| -rw-r--r-- | src/plist.c | 4 |
2 files changed, 117 insertions, 30 deletions
diff --git a/src/oplist.c b/src/oplist.c index 0eea27a..3c48b3a 100644 --- a/src/oplist.c +++ b/src/oplist.c | |||
| @@ -37,8 +37,11 @@ | |||
| 37 | 37 | ||
| 38 | #include "plist.h" | 38 | #include "plist.h" |
| 39 | #include "strbuf.h" | 39 | #include "strbuf.h" |
| 40 | #include "time64.h" | ||
| 40 | #include "hashtable.h" | 41 | #include "hashtable.h" |
| 41 | 42 | ||
| 43 | #define MAC_EPOCH 978307200 | ||
| 44 | |||
| 42 | #ifdef DEBUG | 45 | #ifdef DEBUG |
| 43 | static int plist_ostep_debug = 0; | 46 | static int plist_ostep_debug = 0; |
| 44 | #define PLIST_OSTEP_ERR(...) if (plist_ostep_debug) { fprintf(stderr, "libplist[ostepparser] ERROR: " __VA_ARGS__); } | 47 | #define PLIST_OSTEP_ERR(...) if (plist_ostep_debug) { fprintf(stderr, "libplist[ostepparser] ERROR: " __VA_ARGS__); } |
| @@ -144,7 +147,7 @@ static int str_needs_quotes(const char* str, size_t len) | |||
| 144 | return 0; | 147 | return 0; |
| 145 | } | 148 | } |
| 146 | 149 | ||
| 147 | static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify) | 150 | static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify, int coerce) |
| 148 | { | 151 | { |
| 149 | plist_data_t node_data = NULL; | 152 | plist_data_t node_data = NULL; |
| 150 | 153 | ||
| @@ -235,7 +238,7 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t | |||
| 235 | str_buf_append(*outbuf, " ", 2); | 238 | str_buf_append(*outbuf, " ", 2); |
| 236 | } | 239 | } |
| 237 | } | 240 | } |
| 238 | plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify); | 241 | plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify, coerce); |
| 239 | if (res < 0) { | 242 | if (res < 0) { |
| 240 | return res; | 243 | return res; |
| 241 | } | 244 | } |
| @@ -263,7 +266,7 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t | |||
| 263 | str_buf_append(*outbuf, " ", 2); | 266 | str_buf_append(*outbuf, " ", 2); |
| 264 | } | 267 | } |
| 265 | } | 268 | } |
| 266 | plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify); | 269 | plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify, coerce); |
| 267 | if (res < 0) { | 270 | if (res < 0) { |
| 268 | return res; | 271 | return res; |
| 269 | } | 272 | } |
| @@ -302,19 +305,65 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t | |||
| 302 | str_buf_append(*outbuf, ">", 1); | 305 | str_buf_append(*outbuf, ">", 1); |
| 303 | } break; | 306 | } break; |
| 304 | case PLIST_BOOLEAN: | 307 | case PLIST_BOOLEAN: |
| 305 | PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n"); | 308 | if (coerce) { |
| 306 | return PLIST_ERR_FORMAT; | 309 | if (node_data->boolval) { |
| 310 | str_buf_append(*outbuf, "1", 1); | ||
| 311 | } else { | ||
| 312 | str_buf_append(*outbuf, "0", 1); | ||
| 313 | } | ||
| 314 | } else { | ||
| 315 | PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n"); | ||
| 316 | return PLIST_ERR_FORMAT; | ||
| 317 | } | ||
| 318 | break; | ||
| 307 | case PLIST_NULL: | 319 | case PLIST_NULL: |
| 308 | PLIST_OSTEP_WRITE_ERR("PLIST_NULL type is not valid for OpenStep format\n"); | 320 | if (coerce) { |
| 309 | return PLIST_ERR_FORMAT; | 321 | str_buf_append(*outbuf, "NULL", 4); |
| 322 | } else { | ||
| 323 | PLIST_OSTEP_WRITE_ERR("PLIST_NULL type is not valid for OpenStep format\n"); | ||
| 324 | return PLIST_ERR_FORMAT; | ||
| 325 | } | ||
| 326 | break; | ||
| 310 | case PLIST_DATE: | 327 | case PLIST_DATE: |
| 311 | // NOT VALID FOR OPENSTEP | 328 | if (coerce) { |
| 312 | PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n"); | 329 | Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; |
| 313 | return PLIST_ERR_FORMAT; | 330 | struct TM _btime; |
| 331 | struct TM *btime = gmtime64_r(&timev, &_btime); | ||
| 332 | char datebuf[32]; | ||
| 333 | size_t datelen = 0; | ||
| 334 | if (btime) { | ||
| 335 | struct tm _tmcopy; | ||
| 336 | copy_TM64_to_tm(btime, &_tmcopy); | ||
| 337 | datelen = strftime(datebuf, sizeof(datebuf), "%Y-%m-%dT%H:%M:%SZ", &_tmcopy); | ||
| 338 | } | ||
| 339 | if (datelen <= 0) { | ||
| 340 | datelen = snprintf(datebuf, sizeof(datebuf), "1970-01-01T00:00:00Z"); | ||
| 341 | } | ||
| 342 | str_buf_append(*outbuf, "\"", 1); | ||
| 343 | str_buf_append(*outbuf, datebuf, datelen); | ||
| 344 | str_buf_append(*outbuf, "\"", 1); | ||
| 345 | } else { | ||
| 346 | // NOT VALID FOR OPENSTEP | ||
| 347 | PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n"); | ||
| 348 | return PLIST_ERR_FORMAT; | ||
| 349 | } | ||
| 350 | break; | ||
| 314 | case PLIST_UID: | 351 | case PLIST_UID: |
| 315 | // NOT VALID FOR OPENSTEP | 352 | if (coerce) { |
| 316 | PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n"); | 353 | val = (char*)malloc(64); |
| 317 | return PLIST_ERR_FORMAT; | 354 | if (node_data->length == 16) { |
| 355 | val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); | ||
| 356 | } else { | ||
| 357 | val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); | ||
| 358 | } | ||
| 359 | str_buf_append(*outbuf, val, val_len); | ||
| 360 | free(val); | ||
| 361 | } else { | ||
| 362 | // NOT VALID FOR OPENSTEP | ||
| 363 | PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n"); | ||
| 364 | return PLIST_ERR_FORMAT; | ||
| 365 | } | ||
| 366 | break; | ||
| 318 | default: | 367 | default: |
| 319 | return PLIST_ERR_UNKNOWN; | 368 | return PLIST_ERR_UNKNOWN; |
| 320 | } | 369 | } |
| @@ -360,7 +409,7 @@ static int num_digits_u(uint64_t i) | |||
| 360 | return n; | 409 | return n; |
| 361 | } | 410 | } |
| 362 | 411 | ||
| 363 | static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, hashtable_t *visited) | 412 | static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce, hashtable_t *visited) |
| 364 | { | 413 | { |
| 365 | plist_data_t data; | 414 | plist_data_t data; |
| 366 | if (!node) { | 415 | if (!node) { |
| @@ -385,7 +434,7 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep | |||
| 385 | node_t ch; | 434 | node_t ch; |
| 386 | unsigned int n_children = node_n_children(node); | 435 | unsigned int n_children = node_n_children(node); |
| 387 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { | 436 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { |
| 388 | plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, visited); | 437 | plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, coerce, visited); |
| 389 | if (res < 0) { | 438 | if (res < 0) { |
| 390 | return res; | 439 | return res; |
| 391 | } | 440 | } |
| @@ -442,17 +491,46 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep | |||
| 442 | *size += data->length/4; | 491 | *size += data->length/4; |
| 443 | break; | 492 | break; |
| 444 | case PLIST_BOOLEAN: | 493 | case PLIST_BOOLEAN: |
| 445 | // NOT VALID FOR OPENSTEP | 494 | if (coerce) { |
| 446 | PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n"); | 495 | *size += 1; |
| 447 | return PLIST_ERR_FORMAT; | 496 | } else { |
| 497 | // NOT VALID FOR OPENSTEP | ||
| 498 | PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n"); | ||
| 499 | return PLIST_ERR_FORMAT; | ||
| 500 | } | ||
| 501 | break; | ||
| 448 | case PLIST_DATE: | 502 | case PLIST_DATE: |
| 449 | // NOT VALID FOR OPENSTEP | 503 | if (coerce) { |
| 450 | PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n"); | 504 | // ISO 8601 string: "YYYY-MM-DDTHH:MM:SSZ" = 22 chars max |
| 451 | return PLIST_ERR_FORMAT; | 505 | *size += 24; |
| 506 | } else { | ||
| 507 | // NOT VALID FOR OPENSTEP | ||
| 508 | PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n"); | ||
| 509 | return PLIST_ERR_FORMAT; | ||
| 510 | } | ||
| 511 | break; | ||
| 452 | case PLIST_UID: | 512 | case PLIST_UID: |
| 453 | // NOT VALID FOR OPENSTEP | 513 | if (coerce) { |
| 454 | PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n"); | 514 | if (data->length == 16) { |
| 455 | return PLIST_ERR_FORMAT; | 515 | *size += num_digits_u(data->intval); |
| 516 | } else { | ||
| 517 | *size += num_digits_i((int64_t)data->intval); | ||
| 518 | } | ||
| 519 | } else { | ||
| 520 | // NOT VALID FOR OPENSTEP | ||
| 521 | PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n"); | ||
| 522 | return PLIST_ERR_FORMAT; | ||
| 523 | } | ||
| 524 | break; | ||
| 525 | case PLIST_NULL: | ||
| 526 | if (coerce) { | ||
| 527 | *size += 4; | ||
| 528 | } else { | ||
| 529 | // NOT VALID FOR OPENSTEP | ||
| 530 | PLIST_OSTEP_WRITE_ERR("PLIST_NULL type is not valid for OpenStep format\n"); | ||
| 531 | return PLIST_ERR_FORMAT; | ||
| 532 | } | ||
| 533 | break; | ||
| 456 | default: | 534 | default: |
| 457 | PLIST_OSTEP_WRITE_ERR("invalid node type encountered\n"); | 535 | PLIST_OSTEP_WRITE_ERR("invalid node type encountered\n"); |
| 458 | return PLIST_ERR_UNKNOWN; | 536 | return PLIST_ERR_UNKNOWN; |
| @@ -461,17 +539,23 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep | |||
| 461 | return PLIST_ERR_SUCCESS; | 539 | return PLIST_ERR_SUCCESS; |
| 462 | } | 540 | } |
| 463 | 541 | ||
| 464 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify) | 542 | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce) |
| 465 | { | 543 | { |
| 466 | hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL); | 544 | hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL); |
| 467 | if (!visited) return PLIST_ERR_NO_MEM; | 545 | if (!visited) return PLIST_ERR_NO_MEM; |
| 468 | plist_err_t err = _node_estimate_size(node, size, depth, prettify, visited); | 546 | plist_err_t err = _node_estimate_size(node, size, depth, prettify, coerce, visited); |
| 469 | hash_table_destroy(visited); | 547 | hash_table_destroy(visited); |
| 470 | return err; | 548 | return err; |
| 471 | } | 549 | } |
| 472 | 550 | ||
| 473 | plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify) | 551 | plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify) |
| 474 | { | 552 | { |
| 553 | plist_write_options_t opts = prettify ? PLIST_OPT_NONE : PLIST_OPT_COMPACT; | ||
| 554 | return plist_to_openstep_with_options(plist, openstep, length, opts); | ||
| 555 | } | ||
| 556 | |||
| 557 | plist_err_t plist_to_openstep_with_options(plist_t plist, char **openstep, uint32_t* length, plist_write_options_t options) | ||
| 558 | { | ||
| 475 | uint64_t size = 0; | 559 | uint64_t size = 0; |
| 476 | plist_err_t res; | 560 | plist_err_t res; |
| 477 | 561 | ||
| @@ -479,7 +563,10 @@ plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, | |||
| 479 | return PLIST_ERR_INVALID_ARG; | 563 | return PLIST_ERR_INVALID_ARG; |
| 480 | } | 564 | } |
| 481 | 565 | ||
| 482 | res = node_estimate_size((node_t)plist, &size, 0, prettify); | 566 | int prettify = !(options & PLIST_OPT_COMPACT); |
| 567 | int coerce = options & PLIST_OPT_COERCE; | ||
| 568 | |||
| 569 | res = node_estimate_size((node_t)plist, &size, 0, prettify, coerce); | ||
| 483 | if (res < 0) { | 570 | if (res < 0) { |
| 484 | return res; | 571 | return res; |
| 485 | } | 572 | } |
| @@ -490,7 +577,7 @@ plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, | |||
| 490 | return PLIST_ERR_NO_MEM; | 577 | return PLIST_ERR_NO_MEM; |
| 491 | } | 578 | } |
| 492 | 579 | ||
| 493 | res = node_to_openstep((node_t)plist, &outbuf, 0, prettify); | 580 | res = node_to_openstep((node_t)plist, &outbuf, 0, prettify, coerce); |
| 494 | if (res < 0) { | 581 | if (res < 0) { |
| 495 | str_buf_free(outbuf); | 582 | str_buf_free(outbuf); |
| 496 | *openstep = NULL; | 583 | *openstep = NULL; |
diff --git a/src/plist.c b/src/plist.c index a6d3547..2ad1b0a 100644 --- a/src/plist.c +++ b/src/plist.c | |||
| @@ -2407,7 +2407,7 @@ plist_err_t plist_write_to_string(plist_t plist, char **output, uint32_t* length | |||
| 2407 | err = plist_to_json_with_options(plist, output, length, options); | 2407 | err = plist_to_json_with_options(plist, output, length, options); |
| 2408 | break; | 2408 | break; |
| 2409 | case PLIST_FORMAT_OSTEP: | 2409 | case PLIST_FORMAT_OSTEP: |
| 2410 | err = plist_to_openstep(plist, output, length, ((options & PLIST_OPT_COMPACT) == 0)); | 2410 | err = plist_to_openstep_with_options(plist, output, length, options); |
| 2411 | break; | 2411 | break; |
| 2412 | case PLIST_FORMAT_PRINT: | 2412 | case PLIST_FORMAT_PRINT: |
| 2413 | err = plist_write_to_string_default(plist, output, length, options); | 2413 | err = plist_write_to_string_default(plist, output, length, options); |
| @@ -2445,7 +2445,7 @@ plist_err_t plist_write_to_stream(plist_t plist, FILE *stream, plist_format_t fo | |||
| 2445 | err = plist_to_json_with_options(plist, &output, &length, options); | 2445 | err = plist_to_json_with_options(plist, &output, &length, options); |
| 2446 | break; | 2446 | break; |
| 2447 | case PLIST_FORMAT_OSTEP: | 2447 | case PLIST_FORMAT_OSTEP: |
| 2448 | err = plist_to_openstep(plist, &output, &length, ((options & PLIST_OPT_COMPACT) == 0)); | 2448 | err = plist_to_openstep_with_options(plist, &output, &length, options); |
| 2449 | break; | 2449 | break; |
| 2450 | case PLIST_FORMAT_PRINT: | 2450 | case PLIST_FORMAT_PRINT: |
| 2451 | err = plist_write_to_stream_default(plist, stream, options); | 2451 | err = plist_write_to_stream_default(plist, stream, options); |
