diff options
| author | 2026-02-20 03:38:36 +0100 | |
|---|---|---|
| committer | 2026-02-20 03:38:36 +0100 | |
| commit | e3d5bbdf1f213caafedf185251fba02558d30b98 (patch) | |
| tree | dc62bee53ebb9b3057b2a6fef043684efcefc125 /tools | |
| parent | 30132a9e2ce44f8325d574ea7a3c28466c132f0e (diff) | |
| download | libplist-e3d5bbdf1f213caafedf185251fba02558d30b98.tar.gz libplist-e3d5bbdf1f213caafedf185251fba02558d30b98.tar.bz2 | |
plistutil: Add a --nodepath option to allow selecting a specific node
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/plistutil.c | 70 |
1 files changed, 68 insertions, 2 deletions
diff --git a/tools/plistutil.c b/tools/plistutil.c index 84e7add..6e697cf 100644 --- a/tools/plistutil.c +++ b/tools/plistutil.c | |||
| @@ -40,11 +40,12 @@ | |||
| 40 | #ifdef _MSC_VER | 40 | #ifdef _MSC_VER |
| 41 | #pragma warning(disable:4996) | 41 | #pragma warning(disable:4996) |
| 42 | #define STDIN_FILENO _fileno(stdin) | 42 | #define STDIN_FILENO _fileno(stdin) |
| 43 | #define strtok_r strtok_s | ||
| 43 | #endif | 44 | #endif |
| 44 | 45 | ||
| 45 | typedef struct _options | 46 | typedef struct _options |
| 46 | { | 47 | { |
| 47 | char *in_file, *out_file; | 48 | char *in_file, *out_file, *nodepath; |
| 48 | uint8_t in_fmt, out_fmt; // fmts 0 = undef, 1 = bin, 2 = xml, 3 = json, 4 = openstep | 49 | uint8_t in_fmt, out_fmt; // fmts 0 = undef, 1 = bin, 2 = xml, 3 = json, 4 = openstep |
| 49 | uint8_t flags; | 50 | uint8_t flags; |
| 50 | } options_t; | 51 | } options_t; |
| @@ -70,6 +71,7 @@ static void print_usage(int argc, char *argv[]) | |||
| 70 | printf(" If omitted, XML will be converted to binary,\n"); | 71 | printf(" If omitted, XML will be converted to binary,\n"); |
| 71 | printf(" and binary to XML.\n"); | 72 | printf(" and binary to XML.\n"); |
| 72 | printf(" -p, --print FILE Print the PList in human-readable format.\n"); | 73 | printf(" -p, --print FILE Print the PList in human-readable format.\n"); |
| 74 | printf(" -n, --nodepath PATH Restrict output to nodepath defined by PATH.\n"); | ||
| 73 | printf(" -c, --compact JSON and OpenStep only: Print output in compact form.\n"); | 75 | printf(" -c, --compact JSON and OpenStep only: Print output in compact form.\n"); |
| 74 | printf(" By default, the output will be pretty-printed.\n"); | 76 | printf(" By default, the output will be pretty-printed.\n"); |
| 75 | printf(" -s, --sort Sort all dictionary nodes lexicographically by key\n"); | 77 | printf(" -s, --sort Sort all dictionary nodes lexicographically by key\n"); |
| @@ -96,6 +98,7 @@ static options_t *parse_arguments(int argc, char *argv[]) | |||
| 96 | { "compact", no_argument, 0, 'c' }, | 98 | { "compact", no_argument, 0, 'c' }, |
| 97 | { "sort", no_argument, 0, 's' }, | 99 | { "sort", no_argument, 0, 's' }, |
| 98 | { "print", required_argument, 0, 'p' }, | 100 | { "print", required_argument, 0, 'p' }, |
| 101 | { "nodepath", required_argument, 0, 'n' }, | ||
| 99 | { "debug", no_argument, 0, 'd' }, | 102 | { "debug", no_argument, 0, 'd' }, |
| 100 | { "help", no_argument, 0, 'h' }, | 103 | { "help", no_argument, 0, 'h' }, |
| 101 | { "version", no_argument, 0, 'v' }, | 104 | { "version", no_argument, 0, 'v' }, |
| @@ -103,7 +106,7 @@ static options_t *parse_arguments(int argc, char *argv[]) | |||
| 103 | }; | 106 | }; |
| 104 | 107 | ||
| 105 | int c; | 108 | int c; |
| 106 | while ((c = getopt_long(argc, argv, "i:o:f:csp:dhv", long_options, NULL)) != -1) | 109 | while ((c = getopt_long(argc, argv, "i:o:f:csp:n:dhv", long_options, NULL)) != -1) |
| 107 | { | 110 | { |
| 108 | switch (c) | 111 | switch (c) |
| 109 | { | 112 | { |
| @@ -175,6 +178,15 @@ static options_t *parse_arguments(int argc, char *argv[]) | |||
| 175 | break; | 178 | break; |
| 176 | } | 179 | } |
| 177 | 180 | ||
| 181 | case 'n': | ||
| 182 | if (!optarg || optarg[0] == '\0') { | ||
| 183 | fprintf(stderr, "ERROR: --extract needs a node path\n"); | ||
| 184 | free(options); | ||
| 185 | return NULL; | ||
| 186 | } | ||
| 187 | options->nodepath = optarg; | ||
| 188 | break; | ||
| 189 | |||
| 178 | case 'd': | 190 | case 'd': |
| 179 | options->flags |= OPT_DEBUG; | 191 | options->flags |= OPT_DEBUG; |
| 180 | break; | 192 | break; |
| @@ -326,6 +338,60 @@ int main(int argc, char *argv[]) | |||
| 326 | { | 338 | { |
| 327 | input_res = plist_from_memory(plist_entire, read_size, &root_node, NULL); | 339 | input_res = plist_from_memory(plist_entire, read_size, &root_node, NULL); |
| 328 | if (input_res == PLIST_ERR_SUCCESS) { | 340 | if (input_res == PLIST_ERR_SUCCESS) { |
| 341 | |||
| 342 | if (options->nodepath) { | ||
| 343 | char *copy = strdup(options->nodepath); | ||
| 344 | char *tok, *saveptr = NULL; | ||
| 345 | if (!copy) { | ||
| 346 | plist_free(root_node); | ||
| 347 | free(plist_entire); | ||
| 348 | free(options); | ||
| 349 | return 1; | ||
| 350 | } | ||
| 351 | |||
| 352 | plist_t current = root_node; | ||
| 353 | for (tok = strtok_r(copy, "/", &saveptr); tok; tok = strtok_r(NULL, "/", &saveptr)) { | ||
| 354 | if (*tok == '\0') continue; | ||
| 355 | switch (plist_get_node_type(current)) { | ||
| 356 | case PLIST_DICT: | ||
| 357 | current = plist_dict_get_item(current, tok); | ||
| 358 | break; | ||
| 359 | case PLIST_ARRAY: { | ||
| 360 | char* endp = NULL; | ||
| 361 | uint32_t idx = strtoul(tok, &endp, 10); | ||
| 362 | if (endp == tok || *endp != '\0') { | ||
| 363 | current = NULL; | ||
| 364 | break; | ||
| 365 | } | ||
| 366 | if (idx >= plist_array_get_size(current)) { | ||
| 367 | current = NULL; | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | current = plist_array_get_item(current, idx); | ||
| 371 | break; | ||
| 372 | } | ||
| 373 | default: | ||
| 374 | current = NULL; | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | if (!current) { | ||
| 378 | break; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | free(copy); | ||
| 382 | if (current) { | ||
| 383 | plist_t destnode = plist_copy(current); | ||
| 384 | plist_free(root_node); | ||
| 385 | root_node = destnode; | ||
| 386 | } else { | ||
| 387 | fprintf(stderr, "ERROR: nodepath '%s' is invalid\n", options->nodepath); | ||
| 388 | plist_free(root_node); | ||
| 389 | free(plist_entire); | ||
| 390 | free(options); | ||
| 391 | return 1; | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 329 | if (options->flags & OPT_SORT) { | 395 | if (options->flags & OPT_SORT) { |
| 330 | plist_sort(root_node); | 396 | plist_sort(root_node); |
| 331 | } | 397 | } |
