1 #include <stdio.h>
2 #include "duktape.h"
3 #include "duk_cbor.h"
4
5 #define FMT_CBOR 1
6 #define FMT_JSON 2
7 #define FMT_JX 3
8 #define FMT_JS 4
9
10 static int read_format = 0;
11 static int write_format = 0;
12 static int write_indent = 0;
13
push_stdin(duk_context * ctx,int to_string)14 static void push_stdin(duk_context *ctx, int to_string) {
15 unsigned char *buf;
16 size_t off;
17 size_t len;
18 size_t got;
19
20 off = 0;
21 len = 256;
22 buf = (unsigned char *) duk_push_dynamic_buffer(ctx, len);
23
24 for (;;) {
25 #if 0
26 fprintf(stderr, "reading %ld of input\n", (long) (len - off));
27 #endif
28 got = fread(buf + off, 1, len - off, stdin);
29 if (got == 0U) {
30 break;
31 }
32 off += got;
33 if (len - off < 256U) {
34 size_t newlen = len * 2U;
35 buf = (unsigned char *) duk_resize_buffer(ctx, -1, newlen);
36 len = newlen;
37 }
38 }
39 buf = (unsigned char *) duk_resize_buffer(ctx, -1, off);
40
41 if (to_string) {
42 duk_push_lstring(ctx, (const char *) buf, off);
43 duk_remove(ctx, -2);
44 }
45 }
46
usage_and_exit(void)47 static void usage_and_exit(void) {
48 fprintf(stderr, "Usage: jsoncbor -r cbor|json|jx|js -w cbor|json|jx [--indent N]\n"
49 " jsoncbor -e # shorthand for -r json -w cbor\n"
50 " jsoncbor -d [--indent N] # shorthand for -r cbor -w json [--indent N]\n"
51 "\n"
52 " Input is read from stdin, output is written to stdout.\n"
53 " 'jx' is a Duktape custom JSON extension.\n"
54 " 'js' means evaluate input as an ECMAScript expression.\n");
55 exit(1);
56 }
57
transcode_helper(duk_context * ctx,void * udata)58 static duk_ret_t transcode_helper(duk_context *ctx, void *udata) {
59 const unsigned char *buf;
60 size_t len;
61 duk_idx_t top;
62
63 (void) udata;
64
65 /* For Duktape->JSON conversion map all typed arrays into base-64.
66 * This is generally more useful than the default behavior. However,
67 * the base-64 value doesn't have any kind of 'tag' to allow it to be
68 * parsed back into binary automatically. Right now the CBOR parser
69 * creates plain fixed buffers from incoming binary strings.
70 */
71 duk_eval_string_noresult(ctx,
72 "(function () {\n"
73 " Object.getPrototypeOf(new Uint8Array(0)).toJSON = function () {\n"
74 " return Duktape.enc('base64', this);\n"
75 " };\n"
76 "}())\n");
77
78 top = duk_get_top(ctx);
79
80 if (read_format == FMT_CBOR) {
81 push_stdin(ctx, 0 /*to_string*/);
82 duk_cbor_decode(ctx, -1, 0);
83 } else if (read_format == FMT_JSON) {
84 push_stdin(ctx, 1 /*to_string*/);
85 duk_json_decode(ctx, -1);
86 } else if (read_format == FMT_JX) {
87 duk_eval_string(ctx, "(function(v){return Duktape.dec('jx',v)})");
88 push_stdin(ctx, 1 /*to_string*/);
89 duk_call(ctx, 1);
90 } else if (read_format == FMT_JS) {
91 push_stdin(ctx, 1 /*to_string*/);
92 duk_eval(ctx);
93 } else {
94 (void) duk_type_error(ctx, "invalid read format");
95 }
96
97 if (duk_get_top(ctx) != top + 1) {
98 fprintf(stderr, "top invalid after decoding: %d vs. %d\n", duk_get_top(ctx), top + 1);
99 exit(1);
100 }
101
102 if (write_format == FMT_CBOR) {
103 duk_cbor_encode(ctx, -1, 0);
104 } else if (write_format == FMT_JSON) {
105 duk_eval_string(ctx, "(function(v,i){return JSON.stringify(v,null,i)})");
106 duk_insert(ctx, -2);
107 duk_push_int(ctx, write_indent);
108 duk_call(ctx, 2);
109 } else if (write_format == FMT_JX) {
110 duk_eval_string(ctx, "(function(v,i){return Duktape.enc('jx',v,null,i)})");
111 duk_insert(ctx, -2);
112 duk_push_int(ctx, write_indent);
113 duk_call(ctx, 2);
114 } else {
115 (void) duk_type_error(ctx, "invalid write format");
116 }
117
118 if (duk_is_string(ctx, -1)) {
119 buf = (const unsigned char *) duk_require_lstring(ctx, -1, &len);
120 fwrite((const void *) buf, 1, len, stdout);
121 fprintf(stdout, "\n");
122 } else {
123 buf = (const unsigned char *) duk_require_buffer_data(ctx, -1, &len);
124 fwrite((const void *) buf, 1, len, stdout);
125 }
126
127 if (duk_get_top(ctx) != top + 1) {
128 fprintf(stderr, "top invalid after encoding: %d vs. %d\n", duk_get_top(ctx), top + 1);
129 exit(1);
130 }
131
132 return 0;
133 }
134
parse_format(const char * s)135 static int parse_format(const char *s) {
136 if (strcmp(s, "cbor") == 0) {
137 return FMT_CBOR;
138 } else if (strcmp(s, "json") == 0) {
139 return FMT_JSON;
140 } else if (strcmp(s, "jx") == 0) {
141 return FMT_JX;
142 } else if (strcmp(s, "js") == 0) {
143 return FMT_JS;
144 } else {
145 return 0;
146 }
147 }
148
main(int argc,char * argv[])149 int main(int argc, char *argv[]) {
150 duk_context *ctx;
151 duk_int_t rc;
152 int exitcode = 0;
153 int i;
154
155 for (i = 1; i < argc; i++) {
156 if (strcmp(argv[i], "-e") == 0) {
157 read_format = FMT_JSON;
158 write_format = FMT_CBOR;
159 } else if (strcmp(argv[i], "-d") == 0) {
160 read_format = FMT_CBOR;
161 write_format = FMT_JSON;
162 } else if (strcmp(argv[i], "-r") == 0) {
163 if (i + 1 >= argc) {
164 goto usage;
165 }
166 read_format = parse_format(argv[i + 1]);
167 i++;
168 } else if (strcmp(argv[i], "-w") == 0) {
169 if (i + 1 >= argc) {
170 goto usage;
171 }
172 write_format = parse_format(argv[i + 1]);
173 i++;
174 } else if (strcmp(argv[i], "--indent") == 0) {
175 if (i + 1 >= argc) {
176 goto usage;
177 }
178 write_indent = atoi(argv[i + 1]);
179 i++;
180 } else {
181 goto usage;
182 }
183 }
184
185 if (read_format == 0 || write_format == 0) {
186 goto usage;
187 }
188
189 ctx = duk_create_heap_default();
190 if (!ctx) {
191 return 1;
192 }
193
194 rc = duk_safe_call(ctx, transcode_helper, NULL, 0, 1);
195 if (rc != 0) {
196 fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1));
197 exitcode = 1;
198 }
199 /* duk_pop(ctx): unnecessary */
200
201 duk_destroy_heap(ctx);
202
203 return exitcode;
204
205 usage:
206 usage_and_exit();
207 }
208