diff options
| -rw-r--r-- | docs/plistutil.1 | 46 | ||||
| -rw-r--r-- | tools/plistutil.c | 70 |
2 files changed, 114 insertions, 2 deletions
diff --git a/docs/plistutil.1 b/docs/plistutil.1 index b1f7773..f322bab 100644 --- a/docs/plistutil.1 +++ b/docs/plistutil.1 @@ -29,6 +29,52 @@ convert to/from JSON or OpenStep the output format needs to specified. .B \-p, \-\-print FILE Print PList in human-readable format. .TP +.B \-n, \-\-nodepath PATH +Restrict output to nodepath defined by +.I PATH +which selects a specific node within the plist document. +A node path describes traversal through dictionaries and arrays using / to +separate the components. +Traversal begins at the root node and proceeds from left to right. +Each component selects a child of the current node. +If the current node is a dictionary, the component is interpreted as a +dictionary key name. +If the current node is an array, the component must be a non-negative decimal +integer specifying the array index (0-based). + +Given the following structure: +.PP +.RS +.nf +<plist version="1.0"> +<dict> + <key>Users</key> + <array> + <dict> + <key>Name</key> + <string>Alice</string> + </dict> + <dict> + <key>Name</key> + <string>Bob</string> + </dict> + </array> +</dict> +</plist> +.fi + +A nodepath of: +.TP +\f[B]Users\f[] resolves to the entire array. +.TP +\f[B]Users/0\f[] resolves to the first dictionary in the array. +.TP +\f[B]Users/0/Name\f[] resolves to the string value "Alice". +.TP +\f[B]Users/1/Name\f[] resolves to the string value "Bob". +.RE + +.TP .B \-c, \-\-compact JSON and OpenStep only: Print output in compact form. By default, the output will be pretty-printed. 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 @@ #ifdef _MSC_VER #pragma warning(disable:4996) #define STDIN_FILENO _fileno(stdin) +#define strtok_r strtok_s #endif typedef struct _options { - char *in_file, *out_file; + char *in_file, *out_file, *nodepath; uint8_t in_fmt, out_fmt; // fmts 0 = undef, 1 = bin, 2 = xml, 3 = json, 4 = openstep uint8_t flags; } options_t; @@ -70,6 +71,7 @@ static void print_usage(int argc, char *argv[]) printf(" If omitted, XML will be converted to binary,\n"); printf(" and binary to XML.\n"); printf(" -p, --print FILE Print the PList in human-readable format.\n"); + 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(" -s, --sort Sort all dictionary nodes lexicographically by key\n"); @@ -96,6 +98,7 @@ static options_t *parse_arguments(int argc, char *argv[]) { "compact", no_argument, 0, 'c' }, { "sort", no_argument, 0, 's' }, { "print", required_argument, 0, 'p' }, + { "nodepath", required_argument, 0, 'n' }, { "debug", no_argument, 0, 'd' }, { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, @@ -103,7 +106,7 @@ static options_t *parse_arguments(int argc, char *argv[]) }; int c; - while ((c = getopt_long(argc, argv, "i:o:f:csp:dhv", long_options, NULL)) != -1) + while ((c = getopt_long(argc, argv, "i:o:f:csp:n:dhv", long_options, NULL)) != -1) { switch (c) { @@ -175,6 +178,15 @@ static options_t *parse_arguments(int argc, char *argv[]) break; } + case 'n': + if (!optarg || optarg[0] == '\0') { + fprintf(stderr, "ERROR: --extract needs a node path\n"); + free(options); + return NULL; + } + options->nodepath = optarg; + break; + case 'd': options->flags |= OPT_DEBUG; break; @@ -326,6 +338,60 @@ int main(int argc, char *argv[]) { input_res = plist_from_memory(plist_entire, read_size, &root_node, NULL); if (input_res == PLIST_ERR_SUCCESS) { + + if (options->nodepath) { + char *copy = strdup(options->nodepath); + char *tok, *saveptr = NULL; + if (!copy) { + plist_free(root_node); + free(plist_entire); + free(options); + return 1; + } + + plist_t current = root_node; + for (tok = strtok_r(copy, "/", &saveptr); tok; tok = strtok_r(NULL, "/", &saveptr)) { + if (*tok == '\0') continue; + switch (plist_get_node_type(current)) { + case PLIST_DICT: + current = plist_dict_get_item(current, tok); + break; + case PLIST_ARRAY: { + char* endp = NULL; + uint32_t idx = strtoul(tok, &endp, 10); + if (endp == tok || *endp != '\0') { + current = NULL; + break; + } + if (idx >= plist_array_get_size(current)) { + current = NULL; + break; + } + current = plist_array_get_item(current, idx); + break; + } + default: + current = NULL; + break; + } + if (!current) { + break; + } + } + free(copy); + if (current) { + plist_t destnode = plist_copy(current); + plist_free(root_node); + root_node = destnode; + } else { + fprintf(stderr, "ERROR: nodepath '%s' is invalid\n", options->nodepath); + plist_free(root_node); + free(plist_entire); + free(options); + return 1; + } + } + if (options->flags & OPT_SORT) { plist_sort(root_node); } |
