From 3edac28498d883f1f768699ee15ce85a82bb2a7b Mon Sep 17 00:00:00 2001 From: Calil Khalil Date: Sat, 21 Feb 2026 10:39:24 -0300 Subject: Add JSON coercion support for non-JSON plist types - Add PLIST_OPT_COERCE option to coerce PLIST_DATE, PLIST_DATA, and PLIST_UID to JSON-compatible types (ISO 8601 strings, Base64 strings, and integers) - Add plist_to_json_with_options() function to allow passing coercion options (and others) - Update plist_write_to_string() and plist_write_to_stream() to support coercion option - Add --coerce flag to plistutil for JSON output - Create plist2json symlink that automatically enables coercion when invoked --- tools/plistutil.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'tools/plistutil.c') diff --git a/tools/plistutil.c b/tools/plistutil.c index bdf195e..fef72b7 100644 --- a/tools/plistutil.c +++ b/tools/plistutil.c @@ -52,6 +52,7 @@ typedef struct _options #define OPT_DEBUG (1 << 0) #define OPT_COMPACT (1 << 1) #define OPT_SORT (1 << 2) +#define OPT_COERCE (1 << 3) static void print_usage(int argc, char *argv[]) { @@ -74,6 +75,10 @@ static void print_usage(int argc, char *argv[]) printf(" -n, --nodepath PATH Restrict output to nodepath defined by PATH.\n"); printf(" -c, --compact JSON and OpenStep only: Print output in compact form.\n"); printf(" By default, the output will be pretty-printed.\n"); + printf(" -C, --coerce JSON only: Coerce non-JSON plist types to JSON-compatible\n"); + printf(" representations. Date values become ISO 8601 strings,\n"); + printf(" data values become Base64-encoded strings, and UID values\n"); + printf(" become integers. Implied when invoked as plist2json.\n"); printf(" -s, --sort Sort all dictionary nodes lexicographically by key\n"); printf(" before converting to the output format.\n"); printf(" -d, --debug Enable extended debug output\n"); @@ -96,6 +101,7 @@ static options_t *parse_arguments(int argc, char *argv[]) { "outfile", required_argument, 0, 'o' }, { "format", required_argument, 0, 'f' }, { "compact", no_argument, 0, 'c' }, + { "coerce", no_argument, 0, 'C' }, { "sort", no_argument, 0, 's' }, { "print", required_argument, 0, 'p' }, { "nodepath", required_argument, 0, 'n' }, @@ -106,7 +112,7 @@ static options_t *parse_arguments(int argc, char *argv[]) }; int c; - while ((c = getopt_long(argc, argv, "i:o:f:csp:n:dhv", long_options, NULL)) != -1) + while ((c = getopt_long(argc, argv, "i:o:f:cCsp:n:dhv", long_options, NULL)) != -1) { switch (c) { @@ -154,6 +160,10 @@ static options_t *parse_arguments(int argc, char *argv[]) options->flags |= OPT_COMPACT; break; + case 'C': + options->flags |= OPT_COERCE; + break; + case 's': options->flags |= OPT_SORT; break; @@ -230,6 +240,18 @@ int main(int argc, char *argv[]) return 0; } + // detect invocation as plist2json symlink + { + char *progname = strrchr(argv[0], '/'); + progname = progname ? progname + 1 : argv[0]; + if (!strcmp(progname, "plist2json")) { + if (options->out_fmt == 0) { + options->out_fmt = PLIST_FORMAT_JSON; + } + options->flags |= OPT_COERCE; + } + } + if (options->flags & OPT_DEBUG) { plist_set_debug(1); @@ -404,7 +426,10 @@ int main(int argc, char *argv[]) } else if (options->out_fmt == PLIST_FORMAT_XML) { output_res = plist_to_xml(root_node, &plist_out, &size); } else if (options->out_fmt == PLIST_FORMAT_JSON) { - output_res = plist_to_json(root_node, &plist_out, &size, !(options->flags & OPT_COMPACT)); + plist_write_options_t wropts = PLIST_OPT_NONE; + if (options->flags & OPT_COMPACT) wropts |= PLIST_OPT_COMPACT; + if (options->flags & OPT_COERCE) wropts |= PLIST_OPT_COERCE; + output_res = plist_to_json_with_options(root_node, &plist_out, &size, wropts); } else if (options->out_fmt == PLIST_FORMAT_OSTEP) { output_res = plist_to_openstep(root_node, &plist_out, &size, !(options->flags & OPT_COMPACT)); } else { -- cgit v1.1-32-gdbae