diff options
| author | 2021-12-23 03:09:07 +0100 | |
|---|---|---|
| committer | 2021-12-23 03:09:07 +0100 | |
| commit | 429cbc660ae14d4998715803b44c71abf0e4a339 (patch) | |
| tree | 12fe08f5dcb00a380536198bac3fffd4eb7dd19b | |
| parent | 70002721443dabaa99b56301b537980e137b6249 (diff) | |
| download | libplist-429cbc660ae14d4998715803b44c71abf0e4a339.tar.gz libplist-429cbc660ae14d4998715803b44c71abf0e4a339.tar.bz2 | |
Add support for JSON format
| -rw-r--r-- | docs/plistutil.1 | 14 | ||||
| -rw-r--r-- | include/plist/plist.h | 43 | ||||
| -rw-r--r-- | src/Makefile.am | 2 | ||||
| -rw-r--r-- | src/jplist.c | 695 | ||||
| -rw-r--r-- | src/jsmn.c | 280 | ||||
| -rw-r--r-- | src/jsmn.h | 91 | ||||
| -rw-r--r-- | src/plist.c | 6 | ||||
| -rw-r--r-- | test/Makefile.am | 15 | ||||
| -rwxr-xr-x | test/amp.test | 8 | ||||
| -rw-r--r-- | test/data/data.bplist | bin | 0 -> 3718 bytes | |||
| -rw-r--r-- | test/data/j1.plist | 1 | ||||
| -rwxr-xr-x | test/invalid_tag.test | 8 | ||||
| -rwxr-xr-x | test/json-invalid-types.test | 36 | ||||
| -rwxr-xr-x | test/json1.test | 19 | ||||
| -rwxr-xr-x | test/json2.test | 22 | ||||
| -rwxr-xr-x | test/malformed_dict.test | 8 | ||||
| -rw-r--r-- | test/plist_cmp.c | 4 | ||||
| -rw-r--r-- | test/plist_jtest.c | 131 | ||||
| -rwxr-xr-x | test/recursion.test | 8 | ||||
| -rw-r--r-- | tools/plistutil.c | 78 |
20 files changed, 1403 insertions, 66 deletions
diff --git a/docs/plistutil.1 b/docs/plistutil.1 index e502bd7..eb1b591 100644 --- a/docs/plistutil.1 +++ b/docs/plistutil.1 | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | .TH "plistutil" 1 | 1 | .TH "plistutil" 1 |
| 2 | .SH NAME | 2 | .SH NAME |
| 3 | plistutil \- Convert a plist FILE from binary to XML format or vice-versa | 3 | plistutil \- Convert a plist FILE between binary, XML, and JSON format |
| 4 | .SH SYNOPSIS | 4 | .SH SYNOPSIS |
| 5 | .B plistutil | 5 | .B plistutil |
| 6 | [OPTIONS] | 6 | [OPTIONS] |
| 7 | [-i FILE] | 7 | [-i FILE] |
| 8 | [-o FILE] | 8 | [-o FILE] |
| 9 | .SH DESCRIPTION | 9 | .SH DESCRIPTION |
| 10 | plistutil allows converting a file in Property List format from binary to XML format or vice-versa. | 10 | plistutil allows converting a Property List file between binary, XML, and JSON format. |
| 11 | .SH OPTIONS | 11 | .SH OPTIONS |
| 12 | .TP | 12 | .TP |
| 13 | .B \-i, \-\-infile FILE | 13 | .B \-i, \-\-infile FILE |
| @@ -18,10 +18,13 @@ filename, plistutil will read from stdin. | |||
| 18 | Output FILE to convert to. If this argument is omitted or - is passed as | 18 | Output FILE to convert to. If this argument is omitted or - is passed as |
| 19 | filename, plistutil will write to stdout. | 19 | filename, plistutil will write to stdout. |
| 20 | .TP | 20 | .TP |
| 21 | .B \-f, \-\-format [bin|xml] | 21 | .B \-f, \-\-format [bin|xml|json] |
| 22 | Force output format, regardless of input type. This is useful if the input | 22 | Force output format, regardless of input type. This is useful if the input |
| 23 | format is not known, but the output format should always be in a specific | 23 | format is not known, but the output format should always be in a specific |
| 24 | format (like xml). | 24 | format (like xml or json). |
| 25 | |||
| 26 | If omitted, XML plist data will be converted to binary and vice-versa. To | ||
| 27 | convert to/from JSON the output format needs to specified. | ||
| 25 | .TP | 28 | .TP |
| 26 | .B \-h, \-\-help | 29 | .B \-h, \-\-help |
| 27 | Prints usage information. | 30 | Prints usage information. |
| @@ -47,6 +50,9 @@ Print test.plist as XML plist, regardless of the input format. | |||
| 47 | .B plistutil -i test.plist -f xml -o - | 50 | .B plistutil -i test.plist -f xml -o - |
| 48 | Same as before. | 51 | Same as before. |
| 49 | .TP | 52 | .TP |
| 53 | .B plistutil -i test.plist -f json | ||
| 54 | Print test.plist as JSON plist, regardless of the input format. | ||
| 55 | .TP | ||
| 50 | .B cat test.plist |plistutil -f xml | 56 | .B cat test.plist |plistutil -f xml |
| 51 | Take plist data from stdin - piped via cat - and write the output as XML | 57 | Take plist data from stdin - piped via cat - and write the output as XML |
| 52 | to stdout. | 58 | to stdout. |
diff --git a/include/plist/plist.h b/include/plist/plist.h index 21fd8bd..ac15568 100644 --- a/include/plist/plist.h +++ b/include/plist/plist.h | |||
| @@ -682,6 +682,19 @@ extern "C" | |||
| 682 | plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length); | 682 | plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length); |
| 683 | 683 | ||
| 684 | /** | 684 | /** |
| 685 | * Export the #plist_t structure to JSON format. | ||
| 686 | * | ||
| 687 | * @param plist the root node to export | ||
| 688 | * @param json a pointer to a char* buffer. This function allocates the memory, | ||
| 689 | * caller is responsible for freeing it. | ||
| 690 | * @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer. | ||
| 691 | * @param prettify pretty print the output if != 0 | ||
| 692 | * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure | ||
| 693 | * @note Use plist_mem_free() to free the allocated memory. | ||
| 694 | */ | ||
| 695 | plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify); | ||
| 696 | |||
| 697 | /** | ||
| 685 | * Import the #plist_t structure from XML format. | 698 | * Import the #plist_t structure from XML format. |
| 686 | * | 699 | * |
| 687 | * @param plist_xml a pointer to the xml buffer. | 700 | * @param plist_xml a pointer to the xml buffer. |
| @@ -702,9 +715,25 @@ extern "C" | |||
| 702 | plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist); | 715 | plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist); |
| 703 | 716 | ||
| 704 | /** | 717 | /** |
| 718 | * Import the #plist_t structure from JSON format. | ||
| 719 | * | ||
| 720 | * @param json a pointer to the JSON buffer. | ||
| 721 | * @param length length of the buffer to read. | ||
| 722 | * @param plist a pointer to the imported plist. | ||
| 723 | * @return PLIST_ERR_SUCCESS on success or a #plist_error on failure | ||
| 724 | */ | ||
| 725 | plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist); | ||
| 726 | |||
| 727 | /** | ||
| 705 | * Import the #plist_t structure from memory data. | 728 | * Import the #plist_t structure from memory data. |
| 706 | * This method will look at the first bytes of plist_data | 729 | * This method will look at the first bytes of plist_data |
| 707 | * to determine if plist_data contains a binary or XML plist. | 730 | * to determine if plist_data contains a binary, JSON, or XML plist |
| 731 | * and tries to parse the data in the appropriate format. | ||
| 732 | * @note This is just a convenience function and the format detection is | ||
| 733 | * very basic. It checks with plist_is_binary() if the data supposedly | ||
| 734 | * contains binary plist data, if not it checks if the first byte is | ||
| 735 | * either '{' or '[' and assumes JSON format, otherwise it will try | ||
| 736 | * to parse the data as XML. | ||
| 708 | * | 737 | * |
| 709 | * @param plist_data a pointer to the memory buffer containing plist data. | 738 | * @param plist_data a pointer to the memory buffer containing plist data. |
| 710 | * @param length length of the buffer to read. | 739 | * @param length length of the buffer to read. |
| @@ -714,12 +743,12 @@ extern "C" | |||
| 714 | plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist); | 743 | plist_err_t plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist); |
| 715 | 744 | ||
| 716 | /** | 745 | /** |
| 717 | * Test if in-memory plist data is binary or XML | 746 | * Test if in-memory plist data is in binary format. |
| 718 | * This method will look at the first bytes of plist_data | 747 | * This function will look at the first bytes of plist_data to determine |
| 719 | * to determine if plist_data contains a binary or XML plist. | 748 | * if it supposedly contains a binary plist. |
| 720 | * This method is not validating the whole memory buffer to check if the | 749 | * @note The function is not validating the whole memory buffer to check |
| 721 | * content is truly a plist, it's only using some heuristic on the first few | 750 | * if the content is truly a plist, it is only using some heuristic on |
| 722 | * bytes of plist_data. | 751 | * the first few bytes of plist_data. |
| 723 | * | 752 | * |
| 724 | * @param plist_data a pointer to the memory buffer containing plist data. | 753 | * @param plist_data a pointer to the memory buffer containing plist data. |
| 725 | * @param length length of the buffer to read. | 754 | * @param length length of the buffer to read. |
diff --git a/src/Makefile.am b/src/Makefile.am index 6583add..d4c9e67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
| @@ -22,6 +22,8 @@ libplist_2_0_la_SOURCES = \ | |||
| 22 | time64_limits.h \ | 22 | time64_limits.h \ |
| 23 | xplist.c \ | 23 | xplist.c \ |
| 24 | bplist.c \ | 24 | bplist.c \ |
| 25 | jsmn.c jsmn.h \ | ||
| 26 | jplist.c \ | ||
| 25 | plist.c plist.h | 27 | plist.c plist.h |
| 26 | 28 | ||
| 27 | libplist___2_0_la_LIBADD = libplist-2.0.la | 29 | libplist___2_0_la_LIBADD = libplist-2.0.la |
diff --git a/src/jplist.c b/src/jplist.c new file mode 100644 index 0000000..08441c0 --- /dev/null +++ b/src/jplist.c | |||
| @@ -0,0 +1,695 @@ | |||
| 1 | /* | ||
| 2 | * jplist.c | ||
| 3 | * JSON plist implementation | ||
| 4 | * | ||
| 5 | * Copyright (c) 2019-2021 Nikias Bassen All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifdef HAVE_CONFIG_H | ||
| 23 | #include <config.h> | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #include <string.h> | ||
| 27 | #include <stdlib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <time.h> | ||
| 30 | |||
| 31 | #include <inttypes.h> | ||
| 32 | #include <ctype.h> | ||
| 33 | #include <math.h> | ||
| 34 | #include <limits.h> | ||
| 35 | |||
| 36 | #include <node.h> | ||
| 37 | #include <node_list.h> | ||
| 38 | |||
| 39 | #include "plist.h" | ||
| 40 | #include "strbuf.h" | ||
| 41 | #include "jsmn.h" | ||
| 42 | |||
| 43 | #ifdef DEBUG | ||
| 44 | static int plist_json_debug = 0; | ||
| 45 | #define PLIST_JSON_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonparser] ERROR: " __VA_ARGS__); } | ||
| 46 | #define PLIST_JSON_WRITE_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonwriter] ERROR: " __VA_ARGS__); } | ||
| 47 | #else | ||
| 48 | #define PLIST_JSON_ERR(...) | ||
| 49 | #define PLIST_JSON_WRITE_ERR(...) | ||
| 50 | #endif | ||
| 51 | |||
| 52 | void plist_json_init(void) | ||
| 53 | { | ||
| 54 | /* init JSON stuff */ | ||
| 55 | #ifdef DEBUG | ||
| 56 | char *env_debug = getenv("PLIST_JSON_DEBUG"); | ||
| 57 | if (env_debug && !strcmp(env_debug, "1")) { | ||
| 58 | plist_json_debug = 1; | ||
| 59 | } | ||
| 60 | #endif | ||
| 61 | } | ||
| 62 | |||
| 63 | void plist_json_deinit(void) | ||
| 64 | { | ||
| 65 | /* deinit JSON stuff */ | ||
| 66 | } | ||
| 67 | |||
| 68 | static size_t dtostr(char *buf, size_t bufsize, double realval) | ||
| 69 | { | ||
| 70 | size_t len = 0; | ||
| 71 | if (isnan(realval)) { | ||
| 72 | len = snprintf(buf, bufsize, "nan"); | ||
| 73 | } else if (isinf(realval)) { | ||
| 74 | len = snprintf(buf, bufsize, "%cinfinity", (realval > 0.0) ? '+' : '-'); | ||
| 75 | } else if (realval == 0.0f) { | ||
| 76 | len = snprintf(buf, bufsize, "0.0"); | ||
| 77 | } else { | ||
| 78 | size_t i = 0; | ||
| 79 | len = snprintf(buf, bufsize, "%.*g", 17, realval); | ||
| 80 | for (i = 0; buf && i < len; i++) { | ||
| 81 | if (buf[i] == ',') { | ||
| 82 | buf[i] = '.'; | ||
| 83 | break; | ||
| 84 | } else if (buf[i] == '.') { | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | return len; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int node_to_json(node_t* node, bytearray_t **outbuf, uint32_t depth, int prettify) | ||
| 93 | { | ||
| 94 | plist_data_t node_data = NULL; | ||
| 95 | |||
| 96 | char *val = NULL; | ||
| 97 | size_t val_len = 0; | ||
| 98 | |||
| 99 | uint32_t i = 0; | ||
| 100 | |||
| 101 | if (!node) | ||
| 102 | return PLIST_ERR_INVALID_ARG; | ||
| 103 | |||
| 104 | node_data = plist_get_data(node); | ||
| 105 | |||
| 106 | switch (node_data->type) | ||
| 107 | { | ||
| 108 | case PLIST_BOOLEAN: | ||
| 109 | { | ||
| 110 | if (node_data->boolval) { | ||
| 111 | str_buf_append(*outbuf, "true", 4); | ||
| 112 | } else { | ||
| 113 | str_buf_append(*outbuf, "false", 5); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | break; | ||
| 117 | |||
| 118 | case PLIST_NULL: | ||
| 119 | str_buf_append(*outbuf, "null", 4); | ||
| 120 | break; | ||
| 121 | |||
| 122 | case PLIST_UINT: | ||
| 123 | val = (char*)malloc(64); | ||
| 124 | if (node_data->length == 16) { | ||
| 125 | val_len = snprintf(val, 64, "%"PRIu64, node_data->intval); | ||
| 126 | } else { | ||
| 127 | val_len = snprintf(val, 64, "%"PRIi64, node_data->intval); | ||
| 128 | } | ||
| 129 | str_buf_append(*outbuf, val, val_len); | ||
| 130 | free(val); | ||
| 131 | break; | ||
| 132 | |||
| 133 | case PLIST_REAL: | ||
| 134 | val = (char*)malloc(64); | ||
| 135 | val_len = dtostr(val, 64, node_data->realval); | ||
| 136 | str_buf_append(*outbuf, val, val_len); | ||
| 137 | free(val); | ||
| 138 | break; | ||
| 139 | |||
| 140 | case PLIST_STRING: | ||
| 141 | case PLIST_KEY: { | ||
| 142 | size_t j = 0; | ||
| 143 | size_t len = 0; | ||
| 144 | off_t start = 0; | ||
| 145 | off_t cur = 0; | ||
| 146 | |||
| 147 | str_buf_append(*outbuf, "\"", 1); | ||
| 148 | |||
| 149 | len = node_data->length; | ||
| 150 | for (j = 0; j < len; j++) { | ||
| 151 | switch (node_data->strval[j]) { | ||
| 152 | case '"': | ||
| 153 | str_buf_append(*outbuf, node_data->strval + start, cur - start); | ||
| 154 | str_buf_append(*outbuf, "\\\"", 2); | ||
| 155 | start = cur+1; | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | break; | ||
| 159 | } | ||
| 160 | cur++; | ||
| 161 | } | ||
| 162 | str_buf_append(*outbuf, node_data->strval + start, cur - start); | ||
| 163 | |||
| 164 | str_buf_append(*outbuf, "\"", 1); | ||
| 165 | } break; | ||
| 166 | |||
| 167 | case PLIST_ARRAY: { | ||
| 168 | str_buf_append(*outbuf, "[", 1); | ||
| 169 | node_t *ch; | ||
| 170 | uint32_t cnt = 0; | ||
| 171 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { | ||
| 172 | if (cnt > 0) { | ||
| 173 | str_buf_append(*outbuf, ",", 1); | ||
| 174 | } | ||
| 175 | if (prettify) { | ||
| 176 | str_buf_append(*outbuf, "\n", 1); | ||
| 177 | for (i = 0; i <= depth; i++) { | ||
| 178 | str_buf_append(*outbuf, " ", 2); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | int res = node_to_json(ch, outbuf, depth+1, prettify); | ||
| 182 | if (res < 0) { | ||
| 183 | return res; | ||
| 184 | } | ||
| 185 | cnt++; | ||
| 186 | } | ||
| 187 | if (cnt > 0 && prettify) { | ||
| 188 | str_buf_append(*outbuf, "\n", 1); | ||
| 189 | for (i = 0; i < depth; i++) { | ||
| 190 | str_buf_append(*outbuf, " ", 2); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | str_buf_append(*outbuf, "]", 1); | ||
| 194 | } break; | ||
| 195 | case PLIST_DICT: { | ||
| 196 | str_buf_append(*outbuf, "{", 1); | ||
| 197 | node_t *ch; | ||
| 198 | uint32_t cnt = 0; | ||
| 199 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { | ||
| 200 | if (cnt > 0 && cnt % 2 == 0) { | ||
| 201 | str_buf_append(*outbuf, ",", 1); | ||
| 202 | } | ||
| 203 | if (cnt % 2 == 0 && prettify) { | ||
| 204 | str_buf_append(*outbuf, "\n", 1); | ||
| 205 | for (i = 0; i <= depth; i++) { | ||
| 206 | str_buf_append(*outbuf, " ", 2); | ||
| 207 | } | ||
| 208 | } | ||
| 209 | int res = node_to_json(ch, outbuf, depth+1, prettify); | ||
| 210 | if (res < 0) { | ||
| 211 | return res; | ||
| 212 | } | ||
| 213 | if (cnt % 2 == 0) { | ||
| 214 | str_buf_append(*outbuf, ":", 1); | ||
| 215 | if (prettify) { | ||
| 216 | str_buf_append(*outbuf, " ", 1); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | cnt++; | ||
| 220 | } | ||
| 221 | if (cnt > 0 && prettify) { | ||
| 222 | str_buf_append(*outbuf, "\n", 1); | ||
| 223 | for (i = 0; i < depth; i++) { | ||
| 224 | str_buf_append(*outbuf, " ", 2); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | str_buf_append(*outbuf, "}", 1); | ||
| 228 | } break; | ||
| 229 | case PLIST_DATA: | ||
| 230 | // NOT VALID FOR JSON | ||
| 231 | PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n"); | ||
| 232 | return PLIST_ERR_FORMAT; | ||
| 233 | case PLIST_DATE: | ||
| 234 | // NOT VALID FOR JSON | ||
| 235 | PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n"); | ||
| 236 | return PLIST_ERR_FORMAT; | ||
| 237 | case PLIST_UID: | ||
| 238 | // NOT VALID FOR JSON | ||
| 239 | PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n"); | ||
| 240 | return PLIST_ERR_FORMAT; | ||
| 241 | default: | ||
| 242 | return PLIST_ERR_UNKNOWN; | ||
| 243 | } | ||
| 244 | |||
| 245 | return PLIST_ERR_SUCCESS; | ||
| 246 | } | ||
| 247 | |||
| 248 | #define PO10i_LIMIT (INT64_MAX/10) | ||
| 249 | |||
| 250 | /* based on https://stackoverflow.com/a/4143288 */ | ||
| 251 | static int num_digits_i(int64_t i) | ||
| 252 | { | ||
| 253 | int n; | ||
| 254 | int64_t po10; | ||
| 255 | n=1; | ||
| 256 | if (i < 0) { | ||
| 257 | i = -i; | ||
| 258 | n++; | ||
| 259 | } | ||
| 260 | po10=10; | ||
| 261 | while (i>=po10) { | ||
| 262 | n++; | ||
| 263 | if (po10 > PO10i_LIMIT) break; | ||
| 264 | po10*=10; | ||
| 265 | } | ||
| 266 | return n; | ||
| 267 | } | ||
| 268 | |||
| 269 | #define PO10u_LIMIT (UINT64_MAX/10) | ||
| 270 | |||
| 271 | /* based on https://stackoverflow.com/a/4143288 */ | ||
| 272 | static int num_digits_u(uint64_t i) | ||
| 273 | { | ||
| 274 | int n; | ||
| 275 | uint64_t po10; | ||
| 276 | n=1; | ||
| 277 | po10=10; | ||
| 278 | while (i>=po10) { | ||
| 279 | n++; | ||
| 280 | if (po10 > PO10u_LIMIT) break; | ||
| 281 | po10*=10; | ||
| 282 | } | ||
| 283 | return n; | ||
| 284 | } | ||
| 285 | |||
| 286 | static int node_estimate_size(node_t *node, uint64_t *size, uint32_t depth, int prettify) | ||
| 287 | { | ||
| 288 | plist_data_t data; | ||
| 289 | if (!node) { | ||
| 290 | return PLIST_ERR_INVALID_ARG; | ||
| 291 | } | ||
| 292 | data = plist_get_data(node); | ||
| 293 | if (node->children) { | ||
| 294 | node_t *ch; | ||
| 295 | unsigned int n_children = node_n_children(node); | ||
| 296 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { | ||
| 297 | int res = node_estimate_size(ch, size, depth + 1, prettify); | ||
| 298 | if (res < 0) { | ||
| 299 | return res; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | switch (data->type) { | ||
| 303 | case PLIST_DICT: | ||
| 304 | *size += 2; // '{' and '}' | ||
| 305 | *size += n_children-1; // number of ':' and ',' | ||
| 306 | if (prettify) { | ||
| 307 | *size += n_children; // number of '\n' and extra space | ||
| 308 | *size += n_children * (depth+1); // indent for every 2nd child | ||
| 309 | *size += 1; // additional '\n' | ||
| 310 | } | ||
| 311 | break; | ||
| 312 | case PLIST_ARRAY: | ||
| 313 | *size += 2; // '[' and ']' | ||
| 314 | *size += n_children-1; // number of ',' | ||
| 315 | if (prettify) { | ||
| 316 | *size += n_children; // number of '\n' | ||
| 317 | *size += n_children * ((depth+1)<<1); // indent for every child | ||
| 318 | *size += 1; // additional '\n' | ||
| 319 | } | ||
| 320 | break; | ||
| 321 | default: | ||
| 322 | break; | ||
| 323 | } | ||
| 324 | if (prettify) | ||
| 325 | *size += (depth << 1); // indent for {} and [] | ||
| 326 | } else { | ||
| 327 | switch (data->type) { | ||
| 328 | case PLIST_STRING: | ||
| 329 | case PLIST_KEY: | ||
| 330 | *size += data->length; | ||
| 331 | *size += 2; | ||
| 332 | break; | ||
| 333 | case PLIST_UINT: | ||
| 334 | if (data->length == 16) { | ||
| 335 | *size += num_digits_u(data->intval); | ||
| 336 | } else { | ||
| 337 | *size += num_digits_i((int64_t)data->intval); | ||
| 338 | } | ||
| 339 | break; | ||
| 340 | case PLIST_REAL: | ||
| 341 | *size += dtostr(NULL, 0, data->realval); | ||
| 342 | break; | ||
| 343 | case PLIST_BOOLEAN: | ||
| 344 | *size += ((data->boolval) ? 4 : 5); | ||
| 345 | break; | ||
| 346 | case PLIST_DICT: | ||
| 347 | case PLIST_ARRAY: | ||
| 348 | *size += 2; | ||
| 349 | break; | ||
| 350 | case PLIST_DATA: | ||
| 351 | // NOT VALID FOR JSON | ||
| 352 | PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n"); | ||
| 353 | return PLIST_ERR_FORMAT; | ||
| 354 | case PLIST_DATE: | ||
| 355 | // NOT VALID FOR JSON | ||
| 356 | PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n"); | ||
| 357 | return PLIST_ERR_FORMAT; | ||
| 358 | case PLIST_UID: | ||
| 359 | // NOT VALID FOR JSON | ||
| 360 | PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n"); | ||
| 361 | return PLIST_ERR_FORMAT; | ||
| 362 | default: | ||
| 363 | PLIST_JSON_WRITE_ERR("invalid node type encountered\n"); | ||
| 364 | return PLIST_ERR_UNKNOWN; | ||
| 365 | } | ||
| 366 | } | ||
| 367 | return PLIST_ERR_SUCCESS; | ||
| 368 | } | ||
| 369 | |||
| 370 | PLIST_API int plist_to_json(plist_t plist, char **json, uint32_t* length, int prettify) | ||
| 371 | { | ||
| 372 | uint64_t size = 0; | ||
| 373 | int res; | ||
| 374 | |||
| 375 | if (!plist || !json || !length) { | ||
| 376 | return PLIST_ERR_INVALID_ARG; | ||
| 377 | } | ||
| 378 | |||
| 379 | res = node_estimate_size(plist, &size, 0, prettify); | ||
| 380 | if (res < 0) { | ||
| 381 | return res; | ||
| 382 | } | ||
| 383 | |||
| 384 | strbuf_t *outbuf = str_buf_new(size); | ||
| 385 | if (!outbuf) { | ||
| 386 | PLIST_JSON_WRITE_ERR("Could not allocate output buffer"); | ||
| 387 | return PLIST_ERR_NO_MEM; | ||
| 388 | } | ||
| 389 | |||
| 390 | res = node_to_json(plist, &outbuf, 0, prettify); | ||
| 391 | if (res < 0) { | ||
| 392 | str_buf_free(outbuf); | ||
| 393 | *json = NULL; | ||
| 394 | *length = 0; | ||
| 395 | return res; | ||
| 396 | } | ||
| 397 | |||
| 398 | str_buf_append(outbuf, "\0", 1); | ||
| 399 | |||
| 400 | *json = outbuf->data; | ||
| 401 | *length = outbuf->len - 1; | ||
| 402 | |||
| 403 | outbuf->data = NULL; | ||
| 404 | str_buf_free(outbuf); | ||
| 405 | |||
| 406 | return PLIST_ERR_SUCCESS; | ||
| 407 | } | ||
| 408 | |||
| 409 | static plist_t parse_primitive(const char* js, jsmntok_t* tokens, int* index) | ||
| 410 | { | ||
| 411 | if (tokens[*index].type != JSMN_PRIMITIVE) { | ||
| 412 | PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__); | ||
| 413 | return NULL; | ||
| 414 | } | ||
| 415 | plist_t val = NULL; | ||
| 416 | const char* str_val = js + tokens[*index].start; | ||
| 417 | const char* str_end = js + tokens[*index].end; | ||
| 418 | size_t str_len = tokens[*index].end - tokens[*index].start; | ||
| 419 | if (!strncmp("false", str_val, str_len)) { | ||
| 420 | val = plist_new_bool(0); | ||
| 421 | } else if (!strncmp("true", str_val, str_len)) { | ||
| 422 | val = plist_new_bool(1); | ||
| 423 | } else if (!strncmp("null", str_val, str_len)) { | ||
| 424 | plist_data_t data = plist_new_plist_data(); | ||
| 425 | data->type = PLIST_NULL; | ||
| 426 | val = plist_new_node(data); | ||
| 427 | } else if (str_val[0] == '-' || isdigit(str_val[0])) { | ||
| 428 | char* endp = NULL; | ||
| 429 | long long intpart = strtol(str_val, &endp, 10); | ||
| 430 | if (endp >= str_end) { | ||
| 431 | /* integer */ | ||
| 432 | val = plist_new_uint((uint64_t)intpart); | ||
| 433 | } else if (*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) { | ||
| 434 | /* float */ | ||
| 435 | char* fendp = endp+1; | ||
| 436 | while (isdigit(*fendp) && fendp < str_end) fendp++; | ||
| 437 | if ((fendp > endp+1 && fendp >= str_end) || (fendp+2 < str_end && (*fendp == 'e' || *fendp == 'E') && (*(fendp+1) == '+' || *(fendp+1) == '-') && isdigit(*(fendp+2)))) { | ||
| 438 | double dval = atof(str_val); | ||
| 439 | val = plist_new_real(dval); | ||
| 440 | } else { | ||
| 441 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js)); | ||
| 442 | } | ||
| 443 | } else { | ||
| 444 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js)); | ||
| 445 | } | ||
| 446 | } else { | ||
| 447 | PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val); | ||
| 448 | } | ||
| 449 | (*index)++; | ||
| 450 | return val; | ||
| 451 | } | ||
| 452 | |||
| 453 | static plist_t parse_string(const char* js, jsmntok_t* tokens, int* index) | ||
| 454 | { | ||
| 455 | if (tokens[*index].type != JSMN_STRING) { | ||
| 456 | PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__); | ||
| 457 | return NULL; | ||
| 458 | } | ||
| 459 | |||
| 460 | const char* str_val = js + tokens[*index].start; | ||
| 461 | size_t str_len = tokens[*index].end - tokens[*index].start; | ||
| 462 | char* strval = strndup(str_val, str_len); | ||
| 463 | plist_t node; | ||
| 464 | |||
| 465 | /* unescape */ | ||
| 466 | size_t i = 0; | ||
| 467 | while (i < str_len) { | ||
| 468 | if (strval[i] == '\\' && i < str_len-1) { | ||
| 469 | switch (strval[i+1]) { | ||
| 470 | case '\"': case '/' : case '\\' : case 'b' : | ||
| 471 | case 'f' : case 'r' : case 'n' : case 't' : | ||
| 472 | memmove(strval+i, strval+i+1, str_len - (i+1)); | ||
| 473 | str_len--; | ||
| 474 | switch (strval[i]) { | ||
| 475 | case 'b': | ||
| 476 | strval[i] = '\b'; | ||
| 477 | break; | ||
| 478 | case 'f': | ||
| 479 | strval[i] = '\f'; | ||
| 480 | break; | ||
| 481 | case 'r': | ||
| 482 | strval[i] = '\r'; | ||
| 483 | break; | ||
| 484 | case 'n': | ||
| 485 | strval[i] = '\n'; | ||
| 486 | break; | ||
| 487 | case 't': | ||
| 488 | strval[i] = '\t'; | ||
| 489 | break; | ||
| 490 | default: | ||
| 491 | break; | ||
| 492 | } | ||
| 493 | break; | ||
| 494 | case 'u': { | ||
| 495 | unsigned int val = 0; | ||
| 496 | if (str_len-(i+2) < 4) { | ||
| 497 | free(strval); | ||
| 498 | PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i); | ||
| 499 | return NULL; | ||
| 500 | } | ||
| 501 | if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) { | ||
| 502 | free(strval); | ||
| 503 | PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i); | ||
| 504 | return NULL; | ||
| 505 | } | ||
| 506 | int bytelen = 0; | ||
| 507 | if (val >= 0x800) { | ||
| 508 | /* three bytes */ | ||
| 509 | strval[i] = (char)(0xE0 + ((val >> 12) & 0xF)); | ||
| 510 | strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F)); | ||
| 511 | strval[i+2] = (char)(0x80 + (val & 0x3F)); | ||
| 512 | bytelen = 3; | ||
| 513 | } else if (val >= 0x80) { | ||
| 514 | /* two bytes */ | ||
| 515 | strval[i] = (char)(0xC0 + ((val >> 6) & 0x1F)); | ||
| 516 | strval[i+1] = (char)(0x80 + (val & 0x3F)); | ||
| 517 | bytelen = 2; | ||
| 518 | } else { | ||
| 519 | /* one byte */ | ||
| 520 | strval[i] = (char)(val & 0x7F); | ||
| 521 | bytelen = 1; | ||
| 522 | } | ||
| 523 | memmove(strval+i+bytelen, strval+i+6, str_len - (i+5)); | ||
| 524 | str_len -= (6-bytelen); | ||
| 525 | } break; | ||
| 526 | default: | ||
| 527 | PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i); | ||
| 528 | free(strval); | ||
| 529 | return NULL; | ||
| 530 | } | ||
| 531 | } | ||
| 532 | i++; | ||
| 533 | } | ||
| 534 | |||
| 535 | plist_data_t data = plist_new_plist_data(); | ||
| 536 | data->type = PLIST_STRING; | ||
| 537 | data->strval = strval; | ||
| 538 | data->length = str_len; | ||
| 539 | node = plist_new_node(data); | ||
| 540 | |||
| 541 | (*index)++; | ||
| 542 | return node; | ||
| 543 | } | ||
| 544 | |||
| 545 | static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index); | ||
| 546 | |||
| 547 | static plist_t parse_array(const char* js, jsmntok_t* tokens, int* index) | ||
| 548 | { | ||
| 549 | if (tokens[*index].type != JSMN_ARRAY) { | ||
| 550 | PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__); | ||
| 551 | return NULL; | ||
| 552 | } | ||
| 553 | plist_t arr = plist_new_array(); | ||
| 554 | int num_tokens = tokens[*index].size; | ||
| 555 | int num; | ||
| 556 | int j = (*index)+1; | ||
| 557 | for (num = 0; num < num_tokens; num++) { | ||
| 558 | plist_t val = NULL; | ||
| 559 | switch (tokens[j].type) { | ||
| 560 | case JSMN_OBJECT: | ||
| 561 | val = parse_object(js, tokens, &j); | ||
| 562 | break; | ||
| 563 | case JSMN_ARRAY: | ||
| 564 | val = parse_array(js, tokens, &j); | ||
| 565 | break; | ||
| 566 | case JSMN_STRING: | ||
| 567 | val = parse_string(js, tokens, &j); | ||
| 568 | break; | ||
| 569 | case JSMN_PRIMITIVE: | ||
| 570 | val = parse_primitive(js, tokens, &j); | ||
| 571 | break; | ||
| 572 | default: | ||
| 573 | break; | ||
| 574 | } | ||
| 575 | if (val) { | ||
| 576 | plist_array_append_item(arr, val); | ||
| 577 | } | ||
| 578 | } | ||
| 579 | *(index) = j; | ||
| 580 | return arr; | ||
| 581 | } | ||
| 582 | |||
| 583 | static plist_t parse_object(const char* js, jsmntok_t* tokens, int* index) | ||
| 584 | { | ||
| 585 | if (tokens[*index].type != JSMN_OBJECT) { | ||
| 586 | PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__); | ||
| 587 | return NULL; | ||
| 588 | } | ||
| 589 | plist_t obj = plist_new_dict(); | ||
| 590 | int num_tokens = tokens[*index].size; | ||
| 591 | int num; | ||
| 592 | int j = (*index)+1; | ||
| 593 | for (num = 0; num < num_tokens; num++) { | ||
| 594 | if (tokens[j].type == JSMN_STRING) { | ||
| 595 | char* key = strndup(js + tokens[j].start, tokens[j].end - tokens[j].start); | ||
| 596 | plist_t val = NULL; | ||
| 597 | j++; | ||
| 598 | num++; | ||
| 599 | switch (tokens[j].type) { | ||
| 600 | case JSMN_OBJECT: | ||
| 601 | val = parse_object(js, tokens, &j); | ||
| 602 | break; | ||
| 603 | case JSMN_ARRAY: | ||
| 604 | val = parse_array(js, tokens, &j); | ||
| 605 | break; | ||
| 606 | case JSMN_STRING: | ||
| 607 | val = parse_string(js, tokens, &j); | ||
| 608 | break; | ||
| 609 | case JSMN_PRIMITIVE: | ||
| 610 | val = parse_primitive(js, tokens, &j); | ||
| 611 | break; | ||
| 612 | default: | ||
| 613 | break; | ||
| 614 | } | ||
| 615 | if (val) { | ||
| 616 | plist_dict_set_item(obj, key, val); | ||
| 617 | } | ||
| 618 | free(key); | ||
| 619 | } else { | ||
| 620 | PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__); | ||
| 621 | return NULL; | ||
| 622 | } | ||
| 623 | } | ||
| 624 | (*index) = j; | ||
| 625 | return obj; | ||
| 626 | } | ||
| 627 | |||
| 628 | PLIST_API int plist_from_json(const char *json, uint32_t length, plist_t * plist) | ||
| 629 | { | ||
| 630 | if (!plist) { | ||
| 631 | return PLIST_ERR_INVALID_ARG; | ||
| 632 | } | ||
| 633 | *plist = NULL; | ||
| 634 | if (!json || (length == 0)) { | ||
| 635 | return PLIST_ERR_INVALID_ARG; | ||
| 636 | } | ||
| 637 | |||
| 638 | jsmn_parser parser; | ||
| 639 | jsmn_init(&parser); | ||
| 640 | int maxtoks = 256; | ||
| 641 | int r = 0; | ||
| 642 | jsmntok_t *tokens = NULL; | ||
| 643 | |||
| 644 | do { | ||
| 645 | jsmntok_t* newtokens = realloc(tokens, sizeof(jsmntok_t)*maxtoks); | ||
| 646 | if (!newtokens) { | ||
| 647 | PLIST_JSON_ERR("%s: Out of memory\n", __func__); | ||
| 648 | return PLIST_ERR_NO_MEM; | ||
| 649 | } | ||
| 650 | tokens = newtokens; | ||
| 651 | |||
| 652 | r = jsmn_parse(&parser, json, tokens, maxtoks); | ||
| 653 | if (r == JSMN_ERROR_NOMEM) { | ||
| 654 | maxtoks+=16; | ||
| 655 | continue; | ||
| 656 | } | ||
| 657 | } while (0); | ||
| 658 | |||
| 659 | switch(r) { | ||
| 660 | case JSMN_ERROR_NOMEM: | ||
| 661 | PLIST_JSON_ERR("%s: Out of memory...\n", __func__); | ||
| 662 | free(tokens); | ||
| 663 | return PLIST_ERR_NO_MEM; | ||
| 664 | case JSMN_ERROR_INVAL: | ||
| 665 | PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__); | ||
| 666 | free(tokens); | ||
| 667 | return PLIST_ERR_PARSE; | ||
| 668 | case JSMN_ERROR_PART: | ||
| 669 | PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__); | ||
| 670 | free(tokens); | ||
| 671 | return PLIST_ERR_PARSE; | ||
| 672 | default: | ||
| 673 | break; | ||
| 674 | } | ||
| 675 | |||
| 676 | int startindex = 0; | ||
| 677 | switch (tokens[startindex].type) { | ||
| 678 | case JSMN_PRIMITIVE: | ||
| 679 | *plist = parse_primitive(json, tokens, &startindex); | ||
| 680 | break; | ||
| 681 | case JSMN_STRING: | ||
| 682 | *plist = parse_string(json, tokens, &startindex); | ||
| 683 | break; | ||
| 684 | case JSMN_ARRAY: | ||
| 685 | *plist = parse_array(json, tokens, &startindex); | ||
| 686 | break; | ||
| 687 | case JSMN_OBJECT: | ||
| 688 | *plist = parse_object(json, tokens, &startindex); | ||
| 689 | break; | ||
| 690 | default: | ||
| 691 | break; | ||
| 692 | } | ||
| 693 | free(tokens); | ||
| 694 | return PLIST_ERR_SUCCESS; | ||
| 695 | } | ||
diff --git a/src/jsmn.c b/src/jsmn.c new file mode 100644 index 0000000..ff7c818 --- /dev/null +++ b/src/jsmn.c | |||
| @@ -0,0 +1,280 @@ | |||
| 1 | /* | ||
| 2 | * jsmn.c | ||
| 3 | * Simple JSON parser | ||
| 4 | * | ||
| 5 | * Copyright (c) 2010 Serge A. Zaitsev | ||
| 6 | * | ||
| 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 8 | * of this software and associated documentation files (the "Software"), to deal | ||
| 9 | * in the Software without restriction, including without limitation the rights | ||
| 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 11 | * copies of the Software, and to permit persons to whom the Software is | ||
| 12 | * furnished to do so, subject to the following conditions: | ||
| 13 | * | ||
| 14 | * The above copyright notice and this permission notice shall be included in | ||
| 15 | * all copies or substantial portions of the Software. | ||
| 16 | * | ||
| 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 23 | * THE SOFTWARE. | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <stdlib.h> | ||
| 27 | |||
| 28 | #include "jsmn.h" | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Allocates a fresh unused token from the token pull. | ||
| 32 | */ | ||
| 33 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, | ||
| 34 | jsmntok_t *tokens, int num_tokens) { | ||
| 35 | jsmntok_t *tok; | ||
| 36 | if (parser->toknext >= num_tokens) { | ||
| 37 | return NULL; | ||
| 38 | } | ||
| 39 | tok = &tokens[parser->toknext++]; | ||
| 40 | tok->start = tok->end = -1; | ||
| 41 | tok->size = 0; | ||
| 42 | #ifdef JSMN_PARENT_LINKS | ||
| 43 | tok->parent = -1; | ||
| 44 | #endif | ||
| 45 | return tok; | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Fills token type and boundaries. | ||
| 50 | */ | ||
| 51 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, | ||
| 52 | int start, int end) { | ||
| 53 | token->type = type; | ||
| 54 | token->start = start; | ||
| 55 | token->end = end; | ||
| 56 | token->size = 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Fills next available token with JSON primitive. | ||
| 61 | */ | ||
| 62 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, | ||
| 63 | jsmntok_t *tokens, int num_tokens) { | ||
| 64 | jsmntok_t *token; | ||
| 65 | int start; | ||
| 66 | |||
| 67 | start = parser->pos; | ||
| 68 | |||
| 69 | for (; js[parser->pos] != '\0'; parser->pos++) { | ||
| 70 | switch (js[parser->pos]) { | ||
| 71 | #ifndef JSMN_STRICT | ||
| 72 | /* In strict mode primitive must be followed by "," or "}" or "]" */ | ||
| 73 | case ':': | ||
| 74 | #endif | ||
| 75 | case '\t' : case '\r' : case '\n' : case ' ' : | ||
| 76 | case ',' : case ']' : case '}' : | ||
| 77 | goto found; | ||
| 78 | } | ||
| 79 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { | ||
| 80 | parser->pos = start; | ||
| 81 | return JSMN_ERROR_INVAL; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | #ifdef JSMN_STRICT | ||
| 85 | /* In strict mode primitive must be followed by a comma/object/array */ | ||
| 86 | parser->pos = start; | ||
| 87 | return JSMN_ERROR_PART; | ||
| 88 | #endif | ||
| 89 | |||
| 90 | found: | ||
| 91 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
| 92 | if (token == NULL) { | ||
| 93 | parser->pos = start; | ||
| 94 | return JSMN_ERROR_NOMEM; | ||
| 95 | } | ||
| 96 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); | ||
| 97 | #ifdef JSMN_PARENT_LINKS | ||
| 98 | token->parent = parser->toksuper; | ||
| 99 | #endif | ||
| 100 | parser->pos--; | ||
| 101 | return JSMN_SUCCESS; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Filsl next token with JSON string. | ||
| 106 | */ | ||
| 107 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, | ||
| 108 | jsmntok_t *tokens, int num_tokens) { | ||
| 109 | jsmntok_t *token; | ||
| 110 | |||
| 111 | int start = parser->pos; | ||
| 112 | |||
| 113 | parser->pos++; | ||
| 114 | |||
| 115 | /* Skip starting quote */ | ||
| 116 | for (; js[parser->pos] != '\0'; parser->pos++) { | ||
| 117 | char c = js[parser->pos]; | ||
| 118 | |||
| 119 | /* Quote: end of string */ | ||
| 120 | if (c == '\"') { | ||
| 121 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
| 122 | if (token == NULL) { | ||
| 123 | parser->pos = start; | ||
| 124 | return JSMN_ERROR_NOMEM; | ||
| 125 | } | ||
| 126 | jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); | ||
| 127 | #ifdef JSMN_PARENT_LINKS | ||
| 128 | token->parent = parser->toksuper; | ||
| 129 | #endif | ||
| 130 | return JSMN_SUCCESS; | ||
| 131 | } | ||
| 132 | |||
| 133 | /* Backslash: Quoted symbol expected */ | ||
| 134 | if (c == '\\') { | ||
| 135 | parser->pos++; | ||
| 136 | switch (js[parser->pos]) { | ||
| 137 | /* Allowed escaped symbols */ | ||
| 138 | case '\"': case '/' : case '\\' : case 'b' : | ||
| 139 | case 'f' : case 'r' : case 'n' : case 't' : | ||
| 140 | break; | ||
| 141 | /* Allows escaped symbol \uXXXX */ | ||
| 142 | case 'u': | ||
| 143 | /* TODO */ | ||
| 144 | break; | ||
| 145 | /* Unexpected symbol */ | ||
| 146 | default: | ||
| 147 | parser->pos = start; | ||
| 148 | return JSMN_ERROR_INVAL; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | parser->pos = start; | ||
| 153 | return JSMN_ERROR_PART; | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Parse JSON string and fill tokens. | ||
| 158 | */ | ||
| 159 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, | ||
| 160 | unsigned int num_tokens) { | ||
| 161 | jsmnerr_t r; | ||
| 162 | int i; | ||
| 163 | jsmntok_t *token; | ||
| 164 | |||
| 165 | for (; js[parser->pos] != '\0'; parser->pos++) { | ||
| 166 | char c; | ||
| 167 | jsmntype_t type; | ||
| 168 | |||
| 169 | c = js[parser->pos]; | ||
| 170 | switch (c) { | ||
| 171 | case '{': case '[': | ||
| 172 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
| 173 | if (token == NULL) | ||
| 174 | return JSMN_ERROR_NOMEM; | ||
| 175 | if (parser->toksuper != -1) { | ||
| 176 | tokens[parser->toksuper].size++; | ||
| 177 | #ifdef JSMN_PARENT_LINKS | ||
| 178 | token->parent = parser->toksuper; | ||
| 179 | #endif | ||
| 180 | } | ||
| 181 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); | ||
| 182 | token->start = parser->pos; | ||
| 183 | parser->toksuper = parser->toknext - 1; | ||
| 184 | break; | ||
| 185 | case '}': case ']': | ||
| 186 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); | ||
| 187 | #ifdef JSMN_PARENT_LINKS | ||
| 188 | if (parser->toknext < 1) { | ||
| 189 | return JSMN_ERROR_INVAL; | ||
| 190 | } | ||
| 191 | token = &tokens[parser->toknext - 1]; | ||
| 192 | for (;;) { | ||
| 193 | if (token->start != -1 && token->end == -1) { | ||
| 194 | if (token->type != type) { | ||
| 195 | return JSMN_ERROR_INVAL; | ||
| 196 | } | ||
| 197 | token->end = parser->pos + 1; | ||
| 198 | parser->toksuper = token->parent; | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | if (token->parent == -1) { | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | token = &tokens[token->parent]; | ||
| 205 | } | ||
| 206 | #else | ||
| 207 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
| 208 | token = &tokens[i]; | ||
| 209 | if (token->start != -1 && token->end == -1) { | ||
| 210 | if (token->type != type) { | ||
| 211 | return JSMN_ERROR_INVAL; | ||
| 212 | } | ||
| 213 | parser->toksuper = -1; | ||
| 214 | token->end = parser->pos + 1; | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | /* Error if unmatched closing bracket */ | ||
| 219 | if (i == -1) return JSMN_ERROR_INVAL; | ||
| 220 | for (; i >= 0; i--) { | ||
| 221 | token = &tokens[i]; | ||
| 222 | if (token->start != -1 && token->end == -1) { | ||
| 223 | parser->toksuper = i; | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | #endif | ||
| 228 | break; | ||
| 229 | case '\"': | ||
| 230 | r = jsmn_parse_string(parser, js, tokens, num_tokens); | ||
| 231 | if (r < 0) return r; | ||
| 232 | if (parser->toksuper != -1) | ||
| 233 | tokens[parser->toksuper].size++; | ||
| 234 | break; | ||
| 235 | case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': | ||
| 236 | break; | ||
| 237 | #ifdef JSMN_STRICT | ||
| 238 | /* In strict mode primitives are: numbers and booleans */ | ||
| 239 | case '-': case '0': case '1' : case '2': case '3' : case '4': | ||
| 240 | case '5': case '6': case '7' : case '8': case '9': | ||
| 241 | case 't': case 'f': case 'n' : | ||
| 242 | #else | ||
| 243 | /* In non-strict mode every unquoted value is a primitive */ | ||
| 244 | default: | ||
| 245 | #endif | ||
| 246 | r = jsmn_parse_primitive(parser, js, tokens, num_tokens); | ||
| 247 | if (r < 0) return r; | ||
| 248 | if (parser->toksuper != -1) | ||
| 249 | tokens[parser->toksuper].size++; | ||
| 250 | break; | ||
| 251 | |||
| 252 | #ifdef JSMN_STRICT | ||
| 253 | /* Unexpected char in strict mode */ | ||
| 254 | default: | ||
| 255 | return JSMN_ERROR_INVAL; | ||
| 256 | #endif | ||
| 257 | |||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
| 262 | /* Unmatched opened object or array */ | ||
| 263 | if (tokens[i].start != -1 && tokens[i].end == -1) { | ||
| 264 | return JSMN_ERROR_PART; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | return JSMN_SUCCESS; | ||
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * Creates a new parser based over a given buffer with an array of tokens | ||
| 273 | * available. | ||
| 274 | */ | ||
| 275 | void jsmn_init(jsmn_parser *parser) { | ||
| 276 | parser->pos = 0; | ||
| 277 | parser->toknext = 0; | ||
| 278 | parser->toksuper = -1; | ||
| 279 | } | ||
| 280 | |||
diff --git a/src/jsmn.h b/src/jsmn.h new file mode 100644 index 0000000..f12dc5a --- /dev/null +++ b/src/jsmn.h | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * jsmn.h | ||
| 3 | * Simple JSON parser (header file) | ||
| 4 | * | ||
| 5 | * Copyright (c) 2010 Serge A. Zaitsev | ||
| 6 | * | ||
| 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 8 | * of this software and associated documentation files (the "Software"), to deal | ||
| 9 | * in the Software without restriction, including without limitation the rights | ||
| 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 11 | * copies of the Software, and to permit persons to whom the Software is | ||
| 12 | * furnished to do so, subject to the following conditions: | ||
| 13 | * | ||
| 14 | * The above copyright notice and this permission notice shall be included in | ||
| 15 | * all copies or substantial portions of the Software. | ||
| 16 | * | ||
| 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 23 | * THE SOFTWARE. | ||
| 24 | */ | ||
| 25 | #ifndef __JSMN_H_ | ||
| 26 | #define __JSMN_H_ | ||
| 27 | |||
| 28 | /** | ||
| 29 | * JSON type identifier. Basic types are: | ||
| 30 | * o Object | ||
| 31 | * o Array | ||
| 32 | * o String | ||
| 33 | * o Other primitive: number, boolean (true/false) or null | ||
| 34 | */ | ||
| 35 | typedef enum { | ||
| 36 | JSMN_PRIMITIVE = 0, | ||
| 37 | JSMN_OBJECT = 1, | ||
| 38 | JSMN_ARRAY = 2, | ||
| 39 | JSMN_STRING = 3 | ||
| 40 | } jsmntype_t; | ||
| 41 | |||
| 42 | typedef enum { | ||
| 43 | /* Not enough tokens were provided */ | ||
| 44 | JSMN_ERROR_NOMEM = -1, | ||
| 45 | /* Invalid character inside JSON string */ | ||
| 46 | JSMN_ERROR_INVAL = -2, | ||
| 47 | /* The string is not a full JSON packet, more bytes expected */ | ||
| 48 | JSMN_ERROR_PART = -3, | ||
| 49 | /* Everything was fine */ | ||
| 50 | JSMN_SUCCESS = 0 | ||
| 51 | } jsmnerr_t; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * JSON token description. | ||
| 55 | * @param type type (object, array, string etc.) | ||
| 56 | * @param start start position in JSON data string | ||
| 57 | * @param end end position in JSON data string | ||
| 58 | */ | ||
| 59 | typedef struct { | ||
| 60 | jsmntype_t type; | ||
| 61 | int start; | ||
| 62 | int end; | ||
| 63 | int size; | ||
| 64 | #ifdef JSMN_PARENT_LINKS | ||
| 65 | int parent; | ||
| 66 | #endif | ||
| 67 | } jsmntok_t; | ||
| 68 | |||
| 69 | /** | ||
| 70 | * JSON parser. Contains an array of token blocks available. Also stores | ||
| 71 | * the string being parsed now and current position in that string | ||
| 72 | */ | ||
| 73 | typedef struct { | ||
| 74 | unsigned int pos; /* offset in the JSON string */ | ||
| 75 | int toknext; /* next token to allocate */ | ||
| 76 | int toksuper; /* superior token node, e.g parent object or array */ | ||
| 77 | } jsmn_parser; | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Create JSON parser over an array of tokens | ||
| 81 | */ | ||
| 82 | void jsmn_init(jsmn_parser *parser); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing | ||
| 86 | * a single JSON object. | ||
| 87 | */ | ||
| 88 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, | ||
| 89 | jsmntok_t *tokens, unsigned int num_tokens); | ||
| 90 | |||
| 91 | #endif /* __JSMN_H_ */ | ||
diff --git a/src/plist.c b/src/plist.c index 61b2913..9f3c8f4 100644 --- a/src/plist.c +++ b/src/plist.c | |||
| @@ -49,17 +49,21 @@ extern void plist_xml_init(void); | |||
| 49 | extern void plist_xml_deinit(void); | 49 | extern void plist_xml_deinit(void); |
| 50 | extern void plist_bin_init(void); | 50 | extern void plist_bin_init(void); |
| 51 | extern void plist_bin_deinit(void); | 51 | extern void plist_bin_deinit(void); |
| 52 | extern void plist_json_init(void); | ||
| 53 | extern void plist_json_deinit(void); | ||
| 52 | 54 | ||
| 53 | static void internal_plist_init(void) | 55 | static void internal_plist_init(void) |
| 54 | { | 56 | { |
| 55 | plist_bin_init(); | 57 | plist_bin_init(); |
| 56 | plist_xml_init(); | 58 | plist_xml_init(); |
| 59 | plist_json_init(); | ||
| 57 | } | 60 | } |
| 58 | 61 | ||
| 59 | static void internal_plist_deinit(void) | 62 | static void internal_plist_deinit(void) |
| 60 | { | 63 | { |
| 61 | plist_bin_deinit(); | 64 | plist_bin_deinit(); |
| 62 | plist_xml_deinit(); | 65 | plist_xml_deinit(); |
| 66 | plist_json_deinit(); | ||
| 63 | } | 67 | } |
| 64 | 68 | ||
| 65 | #ifdef WIN32 | 69 | #ifdef WIN32 |
| @@ -195,6 +199,8 @@ PLIST_API plist_err_t plist_from_memory(const char *plist_data, uint32_t length, | |||
| 195 | } | 199 | } |
| 196 | if (plist_is_binary(plist_data, length)) { | 200 | if (plist_is_binary(plist_data, length)) { |
| 197 | res = plist_from_bin(plist_data, length, plist); | 201 | res = plist_from_bin(plist_data, length, plist); |
| 202 | } else if (plist_data[0] == '[' || plist_data[0] == '{') { | ||
| 203 | res = plist_from_json(plist_data, length, plist); | ||
| 198 | } else { | 204 | } else { |
| 199 | res = plist_from_xml(plist_data, length, plist); | 205 | res = plist_from_xml(plist_data, length, plist); |
| 200 | } | 206 | } |
diff --git a/test/Makefile.am b/test/Makefile.am index 3fca55f..b70a85d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am | |||
| @@ -8,7 +8,8 @@ AM_LDFLAGS = | |||
| 8 | noinst_PROGRAMS = \ | 8 | noinst_PROGRAMS = \ |
| 9 | plist_cmp \ | 9 | plist_cmp \ |
| 10 | plist_test \ | 10 | plist_test \ |
| 11 | plist_btest | 11 | plist_btest \ |
| 12 | plist_jtest | ||
| 12 | 13 | ||
| 13 | plist_cmp_SOURCES = plist_cmp.c | 14 | plist_cmp_SOURCES = plist_cmp.c |
| 14 | plist_cmp_LDADD = \ | 15 | plist_cmp_LDADD = \ |
| @@ -21,6 +22,9 @@ plist_test_LDADD = $(top_builddir)/src/libplist-2.0.la | |||
| 21 | plist_btest_SOURCES = plist_btest.c | 22 | plist_btest_SOURCES = plist_btest.c |
| 22 | plist_btest_LDADD = $(top_builddir)/src/libplist-2.0.la | 23 | plist_btest_LDADD = $(top_builddir)/src/libplist-2.0.la |
| 23 | 24 | ||
| 25 | plist_jtest_SOURCES = plist_jtest.c | ||
| 26 | plist_jtest_LDADD = $(top_builddir)/src/libplist-2.0.la | ||
| 27 | |||
| 24 | TESTS = \ | 28 | TESTS = \ |
| 25 | empty.test \ | 29 | empty.test \ |
| 26 | small.test \ | 30 | small.test \ |
| @@ -45,7 +49,10 @@ TESTS = \ | |||
| 45 | offsetsize.test \ | 49 | offsetsize.test \ |
| 46 | refsize.test \ | 50 | refsize.test \ |
| 47 | malformed_dict.test \ | 51 | malformed_dict.test \ |
| 48 | uid.test | 52 | uid.test \ |
| 53 | json1.test \ | ||
| 54 | json2.test \ | ||
| 55 | json-invalid-types.test | ||
| 49 | 56 | ||
| 50 | EXTRA_DIST = \ | 57 | EXTRA_DIST = \ |
| 51 | $(TESTS) \ | 58 | $(TESTS) \ |
| @@ -89,7 +96,9 @@ EXTRA_DIST = \ | |||
| 89 | data/signedunsigned.plist \ | 96 | data/signedunsigned.plist \ |
| 90 | data/unsigned.bplist \ | 97 | data/unsigned.bplist \ |
| 91 | data/unsigned.plist \ | 98 | data/unsigned.plist \ |
| 92 | data/uid.bplist | 99 | data/uid.bplist \ |
| 100 | data/data.bplist \ | ||
| 101 | data/j1.plist | ||
| 93 | 102 | ||
| 94 | TESTS_ENVIRONMENT = \ | 103 | TESTS_ENVIRONMENT = \ |
| 95 | top_srcdir=$(top_srcdir) \ | 104 | top_srcdir=$(top_srcdir) \ |
diff --git a/test/amp.test b/test/amp.test index 0815391..76b32ff 100755 --- a/test/amp.test +++ b/test/amp.test | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | ## -*- sh -*- | 1 | ## -*- sh -*- |
| 2 | 2 | ||
| 3 | set -e | ||
| 4 | |||
| 5 | DATASRC=$top_srcdir/test/data | 3 | DATASRC=$top_srcdir/test/data |
| 6 | TESTFILE=amp.plist | 4 | TESTFILE=amp.plist |
| 7 | DATAIN0=$DATASRC/$TESTFILE | 5 | DATAIN0=$DATASRC/$TESTFILE |
| @@ -9,6 +7,10 @@ DATAOUT0=$top_builddir/test/data/$TESTFILE.out | |||
| 9 | 7 | ||
| 10 | rm -rf $DATAOUT0 | 8 | rm -rf $DATAOUT0 |
| 11 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 | 9 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 |
| 12 | if test -f $DATAOUT0; then | 10 | |
| 11 | # test succeeds if plistutil fails | ||
| 12 | if [ $? -eq 0 ]; then | ||
| 13 | exit 1 | 13 | exit 1 |
| 14 | else | ||
| 15 | exit 0 | ||
| 14 | fi | 16 | fi |
diff --git a/test/data/data.bplist b/test/data/data.bplist new file mode 100644 index 0000000..955993f --- /dev/null +++ b/test/data/data.bplist | |||
| Binary files differ | |||
diff --git a/test/data/j1.plist b/test/data/j1.plist new file mode 100644 index 0000000..2ae9acb --- /dev/null +++ b/test/data/j1.plist | |||
| @@ -0,0 +1 @@ | |||
| {"test":[1,1],"foo":[[1],[1],[1],[1],[[1],[1],[1],[1],[[1],[1],[1],[1]]]],"more":{"a":"yo","b":[{"c":0.25},{"a":"yo","b":[{"c":0.25},{"a":"yo","b":[{"c":0.25}]}]}]}} \ No newline at end of file | |||
diff --git a/test/invalid_tag.test b/test/invalid_tag.test index 079bcdd..2c42a53 100755 --- a/test/invalid_tag.test +++ b/test/invalid_tag.test | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | ## -*- sh -*- | 1 | ## -*- sh -*- |
| 2 | 2 | ||
| 3 | set -e | ||
| 4 | |||
| 5 | DATASRC=$top_srcdir/test/data | 3 | DATASRC=$top_srcdir/test/data |
| 6 | TESTFILE=invalid_tag.plist | 4 | TESTFILE=invalid_tag.plist |
| 7 | DATAIN0=$DATASRC/$TESTFILE | 5 | DATAIN0=$DATASRC/$TESTFILE |
| @@ -9,6 +7,10 @@ DATAOUT0=$top_builddir/test/data/$TESTFILE.out | |||
| 9 | 7 | ||
| 10 | rm -rf $DATAOUT0 | 8 | rm -rf $DATAOUT0 |
| 11 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 | 9 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 |
| 12 | if test -f $DATAOUT0; then | 10 | |
| 11 | # test succeeds if plistutil fails | ||
| 12 | if [ $? -eq 0 ]; then | ||
| 13 | exit 1 | 13 | exit 1 |
| 14 | else | ||
| 15 | exit 0 | ||
| 14 | fi | 16 | fi |
diff --git a/test/json-invalid-types.test b/test/json-invalid-types.test new file mode 100755 index 0000000..0397c05 --- /dev/null +++ b/test/json-invalid-types.test | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | ## -*- sh -*- | ||
| 2 | |||
| 3 | DATASRC=$top_srcdir/test/data | ||
| 4 | DATAOUT=$top_builddir/test/data | ||
| 5 | TESTFILE0=data.bplist | ||
| 6 | TESTFILE1=7.plist | ||
| 7 | TESTFILE2=uid.bplist | ||
| 8 | |||
| 9 | if ! test -d "$DATAOUT"; then | ||
| 10 | mkdir -p $DATAOUT | ||
| 11 | fi | ||
| 12 | |||
| 13 | export PLIST_JSON_DEBUG=1 | ||
| 14 | |||
| 15 | echo "Converting (failure expected)" | ||
| 16 | STDERR=`$top_builddir/tools/plistutil -f json -i $DATASRC/$TESTFILE0 -o /dev/null 2>&1` | ||
| 17 | echo "$STDERR" | ||
| 18 | if ! echo "$STDERR" |grep "PLIST_DATA type is not valid for JSON format"; then | ||
| 19 | exit 1 | ||
| 20 | fi | ||
| 21 | |||
| 22 | echo "Converting (failure expected)" | ||
| 23 | STDERR=`$top_builddir/tools/plistutil -f json -i $DATASRC/$TESTFILE1 -o /dev/null 2>&1` | ||
| 24 | echo "$STDERR" | ||
| 25 | if ! echo "$STDERR" |grep "PLIST_DATE type is not valid for JSON format"; then | ||
| 26 | exit 2 | ||
| 27 | fi | ||
| 28 | |||
| 29 | echo "Converting (failure expected)" | ||
| 30 | STDERR=`$top_builddir/tools/plistutil -f json -i $DATASRC/$TESTFILE2 -o /dev/null 2>&1` | ||
| 31 | echo "$STDERR" | ||
| 32 | if ! echo "$STDERR" |grep "PLIST_UID type is not valid for JSON format"; then | ||
| 33 | exit 3 | ||
| 34 | fi | ||
| 35 | |||
| 36 | exit 0 | ||
diff --git a/test/json1.test b/test/json1.test new file mode 100755 index 0000000..dba4912 --- /dev/null +++ b/test/json1.test | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | ## -*- sh -*- | ||
| 2 | |||
| 3 | set -e | ||
| 4 | |||
| 5 | DATASRC=$top_srcdir/test/data | ||
| 6 | DATAOUT=$top_builddir/test/data | ||
| 7 | TESTFILE=j1.plist | ||
| 8 | |||
| 9 | if ! test -d "$DATAOUT"; then | ||
| 10 | mkdir -p $DATAOUT | ||
| 11 | fi | ||
| 12 | |||
| 13 | export PLIST_JSON_DEBUG=1 | ||
| 14 | |||
| 15 | echo "Converting" | ||
| 16 | $top_builddir/test/plist_jtest $DATASRC/$TESTFILE $DATAOUT/$TESTFILE.out | ||
| 17 | |||
| 18 | echo "Comparing" | ||
| 19 | $top_builddir/test/plist_cmp $DATASRC/$TESTFILE $DATAOUT/$TESTFILE.out | ||
diff --git a/test/json2.test b/test/json2.test new file mode 100755 index 0000000..06a7007 --- /dev/null +++ b/test/json2.test | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | ## -*- sh -*- | ||
| 2 | |||
| 3 | set -e | ||
| 4 | |||
| 5 | DATASRC=$top_srcdir/test/data | ||
| 6 | DATAOUT=$top_builddir/test/data | ||
| 7 | TESTFILE=entities.plist | ||
| 8 | |||
| 9 | if ! test -d "$DATAOUT"; then | ||
| 10 | mkdir -p $DATAOUT | ||
| 11 | fi | ||
| 12 | |||
| 13 | export PLIST_JSON_DEBUG=1 | ||
| 14 | |||
| 15 | echo "Converting input file to JSON" | ||
| 16 | $top_builddir/tools/plistutil -f json -i $DATASRC/$TESTFILE -o $DATASRC/$TESTFILE.json | ||
| 17 | |||
| 18 | echo "Converting to binary and back to JSON" | ||
| 19 | $top_builddir/test/plist_jtest $DATASRC/$TESTFILE.json $DATAOUT/$TESTFILE.json.out | ||
| 20 | |||
| 21 | echo "Comparing" | ||
| 22 | $top_builddir/test/plist_cmp $DATASRC/$TESTFILE $DATAOUT/$TESTFILE.json.out | ||
diff --git a/test/malformed_dict.test b/test/malformed_dict.test index f45ae7e..cbad2bd 100755 --- a/test/malformed_dict.test +++ b/test/malformed_dict.test | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | ## -*- sh -*- | 1 | ## -*- sh -*- |
| 2 | 2 | ||
| 3 | set -e | ||
| 4 | |||
| 5 | DATASRC=$top_srcdir/test/data | 3 | DATASRC=$top_srcdir/test/data |
| 6 | TESTFILE=malformed_dict.bplist | 4 | TESTFILE=malformed_dict.bplist |
| 7 | DATAIN0=$DATASRC/$TESTFILE | 5 | DATAIN0=$DATASRC/$TESTFILE |
| @@ -9,3 +7,9 @@ DATAOUT0=$top_builddir/test/data/$TESTFILE.out | |||
| 9 | 7 | ||
| 10 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 | 8 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 |
| 11 | 9 | ||
| 10 | # test succeeds if plistutil fails | ||
| 11 | if [ $? -eq 0 ]; then | ||
| 12 | exit 1 | ||
| 13 | else | ||
| 14 | exit 0 | ||
| 15 | fi | ||
diff --git a/test/plist_cmp.c b/test/plist_cmp.c index c854446..85b92ee 100644 --- a/test/plist_cmp.c +++ b/test/plist_cmp.c | |||
| @@ -126,11 +126,15 @@ int main(int argc, char *argv[]) | |||
| 126 | 126 | ||
| 127 | if (memcmp(plist_1, "bplist00", 8) == 0) | 127 | if (memcmp(plist_1, "bplist00", 8) == 0) |
| 128 | plist_from_bin(plist_1, size_in1, &root_node1); | 128 | plist_from_bin(plist_1, size_in1, &root_node1); |
| 129 | else if (plist_1[0] == '[' || plist_1[0] == '{') | ||
| 130 | plist_from_json(plist_1, size_in1, &root_node1); | ||
| 129 | else | 131 | else |
| 130 | plist_from_xml(plist_1, size_in1, &root_node1); | 132 | plist_from_xml(plist_1, size_in1, &root_node1); |
| 131 | 133 | ||
| 132 | if (memcmp(plist_2, "bplist00", 8) == 0) | 134 | if (memcmp(plist_2, "bplist00", 8) == 0) |
| 133 | plist_from_bin(plist_2, size_in2, &root_node2); | 135 | plist_from_bin(plist_2, size_in2, &root_node2); |
| 136 | else if (plist_2[0] == '[' || plist_2[0] == '{') | ||
| 137 | plist_from_json(plist_2, size_in2, &root_node2); | ||
| 134 | else | 138 | else |
| 135 | plist_from_xml(plist_2, size_in2, &root_node2); | 139 | plist_from_xml(plist_2, size_in2, &root_node2); |
| 136 | 140 | ||
diff --git a/test/plist_jtest.c b/test/plist_jtest.c new file mode 100644 index 0000000..130e3c7 --- /dev/null +++ b/test/plist_jtest.c | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | /* | ||
| 2 | * backup_test.c | ||
| 3 | * source libplist regression test | ||
| 4 | * | ||
| 5 | * Copyright (c) 2009 Jonathan Beck All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | |||
| 23 | #include "plist/plist.h" | ||
| 24 | |||
| 25 | #include <stdio.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | #include <string.h> | ||
| 28 | #include <sys/stat.h> | ||
| 29 | |||
| 30 | #ifdef _MSC_VER | ||
| 31 | #pragma warning(disable:4996) | ||
| 32 | #endif | ||
| 33 | |||
| 34 | |||
| 35 | int main(int argc, char *argv[]) | ||
| 36 | { | ||
| 37 | FILE *iplist = NULL; | ||
| 38 | plist_t root_node1 = NULL; | ||
| 39 | plist_t root_node2 = NULL; | ||
| 40 | char *plist_json = NULL; | ||
| 41 | char *plist_json2 = NULL; | ||
| 42 | char *plist_bin = NULL; | ||
| 43 | int size_in = 0; | ||
| 44 | uint32_t size_out = 0; | ||
| 45 | uint32_t size_out2 = 0; | ||
| 46 | char *file_in = NULL; | ||
| 47 | char *file_out = NULL; | ||
| 48 | struct stat *filestats = (struct stat *) malloc(sizeof(struct stat)); | ||
| 49 | if (argc != 3) | ||
| 50 | { | ||
| 51 | printf("Wrong input\n"); | ||
| 52 | return 1; | ||
| 53 | } | ||
| 54 | |||
| 55 | file_in = argv[1]; | ||
| 56 | file_out = argv[2]; | ||
| 57 | //read input file | ||
| 58 | iplist = fopen(file_in, "rb"); | ||
| 59 | |||
| 60 | if (!iplist) | ||
| 61 | { | ||
| 62 | printf("File does not exists\n"); | ||
| 63 | return 2; | ||
| 64 | } | ||
| 65 | printf("File %s is open\n", file_in); | ||
| 66 | stat(file_in, filestats); | ||
| 67 | size_in = filestats->st_size; | ||
| 68 | plist_json = (char *) malloc(sizeof(char) * (size_in + 1)); | ||
| 69 | fread(plist_json, sizeof(char), size_in, iplist); | ||
| 70 | fclose(iplist); | ||
| 71 | plist_json[size_in] = 0; | ||
| 72 | |||
| 73 | //convert one format to another | ||
| 74 | plist_from_json(plist_json, size_in, &root_node1); | ||
| 75 | if (!root_node1) | ||
| 76 | { | ||
| 77 | printf("PList JSON parsing failed\n"); | ||
| 78 | return 3; | ||
| 79 | } | ||
| 80 | |||
| 81 | printf("PList JSON parsing succeeded\n"); | ||
| 82 | plist_to_bin(root_node1, &plist_bin, &size_out); | ||
| 83 | if (!plist_bin) | ||
| 84 | { | ||
| 85 | printf("PList BIN writing failed\n"); | ||
| 86 | return 4; | ||
| 87 | } | ||
| 88 | |||
| 89 | printf("PList BIN writing succeeded\n"); | ||
| 90 | plist_from_bin(plist_bin, size_out, &root_node2); | ||
| 91 | if (!root_node2) | ||
| 92 | { | ||
| 93 | printf("PList BIN parsing failed\n"); | ||
| 94 | return 5; | ||
| 95 | } | ||
| 96 | |||
| 97 | printf("PList BIN parsing succeeded\n"); | ||
| 98 | plist_to_json(root_node2, &plist_json2, &size_out2, 0); | ||
| 99 | if (!plist_json2) | ||
| 100 | { | ||
| 101 | printf("PList JSON writing failed\n"); | ||
| 102 | return 8; | ||
| 103 | } | ||
| 104 | |||
| 105 | printf("PList JSON writing succeeded\n"); | ||
| 106 | if (plist_json2) | ||
| 107 | { | ||
| 108 | FILE *oplist = NULL; | ||
| 109 | oplist = fopen(file_out, "wb"); | ||
| 110 | fwrite(plist_json2, size_out2, sizeof(char), oplist); | ||
| 111 | fclose(oplist); | ||
| 112 | } | ||
| 113 | |||
| 114 | plist_free(root_node1); | ||
| 115 | plist_free(root_node2); | ||
| 116 | free(plist_bin); | ||
| 117 | free(plist_json); | ||
| 118 | free(plist_json2); | ||
| 119 | free(filestats); | ||
| 120 | |||
| 121 | if ((uint32_t)size_in != size_out2) | ||
| 122 | { | ||
| 123 | printf("Size of input and output is different\n"); | ||
| 124 | printf("Input size : %i\n", size_in); | ||
| 125 | printf("Output size : %i\n", size_out2); | ||
| 126 | } | ||
| 127 | |||
| 128 | //success | ||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
diff --git a/test/recursion.test b/test/recursion.test index 0120a12..9946d81 100755 --- a/test/recursion.test +++ b/test/recursion.test | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | ## -*- sh -*- | 1 | ## -*- sh -*- |
| 2 | 2 | ||
| 3 | set -e | ||
| 4 | |||
| 5 | DATASRC=$top_srcdir/test/data | 3 | DATASRC=$top_srcdir/test/data |
| 6 | TESTFILE=recursion.bplist | 4 | TESTFILE=recursion.bplist |
| 7 | DATAIN0=$DATASRC/$TESTFILE | 5 | DATAIN0=$DATASRC/$TESTFILE |
| @@ -9,3 +7,9 @@ DATAOUT0=$top_builddir/test/data/$TESTFILE.out | |||
| 9 | 7 | ||
| 10 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 | 8 | $top_builddir/tools/plistutil -i $DATAIN0 -o $DATAOUT0 |
| 11 | 9 | ||
| 10 | # test succeeds if plistutil fails | ||
| 11 | if [ $? -eq 0 ]; then | ||
| 12 | exit 1 | ||
| 13 | else | ||
| 14 | exit 0 | ||
| 15 | fi | ||
diff --git a/tools/plistutil.c b/tools/plistutil.c index 71972f9..bd83e92 100644 --- a/tools/plistutil.c +++ b/tools/plistutil.c | |||
| @@ -41,7 +41,7 @@ | |||
| 41 | typedef struct _options | 41 | typedef struct _options |
| 42 | { | 42 | { |
| 43 | char *in_file, *out_file; | 43 | char *in_file, *out_file; |
| 44 | uint8_t debug, in_fmt, out_fmt; // fmts 0 = undef, 1 = bin, 2 = xml, 3 = json someday | 44 | uint8_t debug, in_fmt, out_fmt; // fmts 0 = undef, 1 = bin, 2 = xml, 3 = json |
| 45 | } options_t; | 45 | } options_t; |
| 46 | 46 | ||
| 47 | static void print_usage(int argc, char *argv[]) | 47 | static void print_usage(int argc, char *argv[]) |
| @@ -50,14 +50,19 @@ static void print_usage(int argc, char *argv[]) | |||
| 50 | name = strrchr(argv[0], '/'); | 50 | name = strrchr(argv[0], '/'); |
| 51 | printf("Usage: %s [OPTIONS] [-i FILE] [-o FILE]\n", (name ? name + 1: argv[0])); | 51 | printf("Usage: %s [OPTIONS] [-i FILE] [-o FILE]\n", (name ? name + 1: argv[0])); |
| 52 | printf("\n"); | 52 | printf("\n"); |
| 53 | printf("Convert a plist FILE from binary to XML format or vice-versa.\n"); | 53 | printf("Convert a plist FILE between binary, XML, and JSON format.\n"); |
| 54 | printf("If -f is omitted, XML plist data will be converted to binary and vice-versa.\n"); | ||
| 55 | printf("To convert to/from JSON the output format needs to be specified.\n"); | ||
| 54 | printf("\n"); | 56 | printf("\n"); |
| 55 | printf("OPTIONS:\n"); | 57 | printf("OPTIONS:\n"); |
| 56 | printf(" -i, --infile FILE Optional FILE to convert from or stdin if - or not used\n"); | 58 | printf(" -i, --infile FILE Optional FILE to convert from or stdin if - or not used\n"); |
| 57 | printf(" -o, --outfile FILE Optional FILE to convert to or stdout if - or not used\n"); | 59 | printf(" -o, --outfile FILE Optional FILE to convert to or stdout if - or not used\n"); |
| 58 | printf(" -f, --format [bin|xml] Force output format, regardless of input type\n"); | 60 | printf(" -f, --format FORMAT Force output format, regardless of input type\n"); |
| 59 | printf(" -d, --debug Enable extended debug output\n"); | 61 | printf(" FORMAT is one of xml, bin, or json\n"); |
| 60 | printf(" -v, --version Print version information\n"); | 62 | printf(" If omitted XML will be converted to binary,\n"); |
| 63 | printf(" and binary to XML.\n"); | ||
| 64 | printf(" -d, --debug Enable extended debug output\n"); | ||
| 65 | printf(" -v, --version Print version information\n"); | ||
| 61 | printf("\n"); | 66 | printf("\n"); |
| 62 | printf("Homepage: <" PACKAGE_URL ">\n"); | 67 | printf("Homepage: <" PACKAGE_URL ">\n"); |
| 63 | printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n"); | 68 | printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n"); |
| @@ -105,8 +110,10 @@ static options_t *parse_arguments(int argc, char *argv[]) | |||
| 105 | options->out_fmt = 1; | 110 | options->out_fmt = 1; |
| 106 | } else if (!strncmp(argv[i+1], "xml", 3)) { | 111 | } else if (!strncmp(argv[i+1], "xml", 3)) { |
| 107 | options->out_fmt = 2; | 112 | options->out_fmt = 2; |
| 113 | } else if (!strncmp(argv[i+1], "json", 4)) { | ||
| 114 | options->out_fmt = 3; | ||
| 108 | } else { | 115 | } else { |
| 109 | printf("ERROR: Unsupported output format\n"); | 116 | fprintf(stderr, "ERROR: Unsupported output format\n"); |
| 110 | free(options); | 117 | free(options); |
| 111 | return NULL; | 118 | return NULL; |
| 112 | } | 119 | } |
| @@ -129,7 +136,7 @@ static options_t *parse_arguments(int argc, char *argv[]) | |||
| 129 | } | 136 | } |
| 130 | else | 137 | else |
| 131 | { | 138 | { |
| 132 | printf("ERROR: Invalid option '%s'\n", argv[i]); | 139 | fprintf(stderr, "ERROR: Invalid option '%s'\n", argv[i]); |
| 133 | free(options); | 140 | free(options); |
| 134 | return NULL; | 141 | return NULL; |
| 135 | } | 142 | } |
| @@ -140,6 +147,7 @@ static options_t *parse_arguments(int argc, char *argv[]) | |||
| 140 | 147 | ||
| 141 | int main(int argc, char *argv[]) | 148 | int main(int argc, char *argv[]) |
| 142 | { | 149 | { |
| 150 | int ret = 0; | ||
| 143 | FILE *iplist = NULL; | 151 | FILE *iplist = NULL; |
| 144 | plist_t root_node = NULL; | 152 | plist_t root_node = NULL; |
| 145 | char *plist_out = NULL; | 153 | char *plist_out = NULL; |
| @@ -162,7 +170,7 @@ int main(int argc, char *argv[]) | |||
| 162 | plist_entire = malloc(sizeof(char) * read_capacity); | 170 | plist_entire = malloc(sizeof(char) * read_capacity); |
| 163 | if(plist_entire == NULL) | 171 | if(plist_entire == NULL) |
| 164 | { | 172 | { |
| 165 | printf("ERROR: Failed to allocate buffer to read from stdin"); | 173 | fprintf(stderr, "ERROR: Failed to allocate buffer to read from stdin"); |
| 166 | free(options); | 174 | free(options); |
| 167 | return 1; | 175 | return 1; |
| 168 | } | 176 | } |
| @@ -176,7 +184,7 @@ int main(int argc, char *argv[]) | |||
| 176 | plist_entire = realloc(plist_entire, sizeof(char) * read_capacity); | 184 | plist_entire = realloc(plist_entire, sizeof(char) * read_capacity); |
| 177 | if (plist_entire == NULL) | 185 | if (plist_entire == NULL) |
| 178 | { | 186 | { |
| 179 | printf("ERROR: Failed to reallocate stdin buffer\n"); | 187 | fprintf(stderr, "ERROR: Failed to reallocate stdin buffer\n"); |
| 180 | free(old); | 188 | free(old); |
| 181 | free(options); | 189 | free(options); |
| 182 | return 1; | 190 | return 1; |
| @@ -190,7 +198,7 @@ int main(int argc, char *argv[]) | |||
| 190 | plist_entire = realloc(plist_entire, sizeof(char) * (read_capacity+1)); | 198 | plist_entire = realloc(plist_entire, sizeof(char) * (read_capacity+1)); |
| 191 | if (plist_entire == NULL) | 199 | if (plist_entire == NULL) |
| 192 | { | 200 | { |
| 193 | printf("ERROR: Failed to reallocate stdin buffer\n"); | 201 | fprintf(stderr, "ERROR: Failed to reallocate stdin buffer\n"); |
| 194 | free(old); | 202 | free(old); |
| 195 | free(options); | 203 | free(options); |
| 196 | return 1; | 204 | return 1; |
| @@ -201,14 +209,14 @@ int main(int argc, char *argv[]) | |||
| 201 | // Not positive we need this, but it doesnt seem to hurt lol | 209 | // Not positive we need this, but it doesnt seem to hurt lol |
| 202 | if(ferror(stdin)) | 210 | if(ferror(stdin)) |
| 203 | { | 211 | { |
| 204 | printf("ERROR: reading from stdin.\n"); | 212 | fprintf(stderr, "ERROR: reading from stdin.\n"); |
| 205 | free(plist_entire); | 213 | free(plist_entire); |
| 206 | free(options); | 214 | free(options); |
| 207 | return 1; | 215 | return 1; |
| 208 | } | 216 | } |
| 209 | 217 | ||
| 210 | if (read_size < 8) { | 218 | if (read_size < 8) { |
| 211 | printf("ERROR: Input file is too small to contain valid plist data.\n"); | 219 | fprintf(stderr, "ERROR: Input file is too small to contain valid plist data.\n"); |
| 212 | free(plist_entire); | 220 | free(plist_entire); |
| 213 | free(options); | 221 | free(options); |
| 214 | return 1; | 222 | return 1; |
| @@ -219,7 +227,7 @@ int main(int argc, char *argv[]) | |||
| 219 | // read input file | 227 | // read input file |
| 220 | iplist = fopen(options->in_file, "rb"); | 228 | iplist = fopen(options->in_file, "rb"); |
| 221 | if (!iplist) { | 229 | if (!iplist) { |
| 222 | printf("ERROR: Could not open input file '%s': %s\n", options->in_file, strerror(errno)); | 230 | fprintf(stderr, "ERROR: Could not open input file '%s': %s\n", options->in_file, strerror(errno)); |
| 223 | free(options); | 231 | free(options); |
| 224 | return 1; | 232 | return 1; |
| 225 | } | 233 | } |
| @@ -228,7 +236,7 @@ int main(int argc, char *argv[]) | |||
| 228 | fstat(fileno(iplist), &filestats); | 236 | fstat(fileno(iplist), &filestats); |
| 229 | 237 | ||
| 230 | if (filestats.st_size < 8) { | 238 | if (filestats.st_size < 8) { |
| 231 | printf("ERROR: Input file is too small to contain valid plist data.\n"); | 239 | fprintf(stderr, "ERROR: Input file is too small to contain valid plist data.\n"); |
| 232 | free(options); | 240 | free(options); |
| 233 | fclose(iplist); | 241 | fclose(iplist); |
| 234 | return -1; | 242 | return -1; |
| @@ -240,7 +248,7 @@ int main(int argc, char *argv[]) | |||
| 240 | } | 248 | } |
| 241 | 249 | ||
| 242 | if (options->out_fmt == 0) { | 250 | if (options->out_fmt == 0) { |
| 243 | // convert from binary to xml or vice-versa<br> | 251 | // convert from binary to xml or vice-versa |
| 244 | if (plist_is_binary(plist_entire, read_size)) | 252 | if (plist_is_binary(plist_entire, read_size)) |
| 245 | { | 253 | { |
| 246 | plist_from_bin(plist_entire, read_size, &root_node); | 254 | plist_from_bin(plist_entire, read_size, &root_node); |
| @@ -254,29 +262,13 @@ int main(int argc, char *argv[]) | |||
| 254 | } | 262 | } |
| 255 | else | 263 | else |
| 256 | { | 264 | { |
| 265 | plist_from_memory(plist_entire, read_size, &root_node); | ||
| 257 | if (options->out_fmt == 1) { | 266 | if (options->out_fmt == 1) { |
| 258 | if (plist_is_binary(plist_entire, read_size)) | 267 | plist_to_bin(root_node, &plist_out, &size); |
| 259 | { | ||
| 260 | plist_out = malloc(sizeof(char) * read_size); | ||
| 261 | memcpy(plist_out, plist_entire, read_size); | ||
| 262 | size = read_size; | ||
| 263 | } | ||
| 264 | else | ||
| 265 | { | ||
| 266 | plist_from_xml(plist_entire, read_size, &root_node); | ||
| 267 | plist_to_bin(root_node, &plist_out, &size); | ||
| 268 | } | ||
| 269 | } else if (options->out_fmt == 2) { | 268 | } else if (options->out_fmt == 2) { |
| 270 | if (plist_is_binary(plist_entire, read_size)) { | 269 | plist_to_xml(root_node, &plist_out, &size); |
| 271 | plist_from_bin(plist_entire, read_size, &root_node); | 270 | } else if (options->out_fmt == 3) { |
| 272 | plist_to_xml(root_node, &plist_out, &size); | 271 | plist_to_json(root_node, &plist_out, &size, 0); |
| 273 | } | ||
| 274 | else | ||
| 275 | { | ||
| 276 | plist_out = malloc(sizeof(char) * read_size); | ||
| 277 | memcpy(plist_out, plist_entire, read_size); | ||
| 278 | size = read_size; | ||
| 279 | } | ||
| 280 | } | 272 | } |
| 281 | } | 273 | } |
| 282 | plist_free(root_node); | 274 | plist_free(root_node); |
| @@ -288,7 +280,7 @@ int main(int argc, char *argv[]) | |||
| 288 | { | 280 | { |
| 289 | FILE *oplist = fopen(options->out_file, "wb"); | 281 | FILE *oplist = fopen(options->out_file, "wb"); |
| 290 | if (!oplist) { | 282 | if (!oplist) { |
| 291 | printf("ERROR: Could not open output file '%s': %s\n", options->out_file, strerror(errno)); | 283 | fprintf(stderr, "ERROR: Could not open output file '%s': %s\n", options->out_file, strerror(errno)); |
| 292 | free(options); | 284 | free(options); |
| 293 | return 1; | 285 | return 1; |
| 294 | } | 286 | } |
| @@ -301,9 +293,11 @@ int main(int argc, char *argv[]) | |||
| 301 | 293 | ||
| 302 | free(plist_out); | 294 | free(plist_out); |
| 303 | } | 295 | } |
| 304 | else | 296 | else { |
| 305 | printf("ERROR: Failed to convert input file.\n"); | 297 | fprintf(stderr, "ERROR: Failed to convert input file.\n"); |
| 298 | ret = 2; | ||
| 299 | } | ||
| 306 | 300 | ||
| 307 | free(options); | 301 | free(options); |
| 308 | return 0; | 302 | return ret; |
| 309 | } | 303 | } |
