1 #include <assert.h>
2 #include <stdio.h>
3 #include <float.h>
4 #include <string.h>
5 
6 #ifdef WIN32
7 #include <windows.h>
8 #include <io.h>
9 #include <fileapi.h>
10 #endif
11 
12 #include "jv.h"
13 #include "jv_dtoa.h"
14 #include "jv_unicode.h"
15 #include "jv_alloc.h"
16 
17 #ifndef MAX_PRINT_DEPTH
18 #define MAX_PRINT_DEPTH (256)
19 #endif
20 
21 #define ESC "\033"
22 #define COL(c) (ESC "[" c "m")
23 #define COLRESET (ESC "[0m")
24 
25 // Color table. See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
26 // for how to choose these.
27 static const jv_kind color_kinds[] =
28   {JV_KIND_NULL,   JV_KIND_FALSE, JV_KIND_TRUE, JV_KIND_NUMBER,
29    JV_KIND_STRING, JV_KIND_ARRAY, JV_KIND_OBJECT};
30 static char color_bufs[sizeof(color_kinds)/sizeof(color_kinds[0])][16];
31 static const char *color_bufps[8];
32 static const char* def_colors[] =
33   {COL("1;30"),    COL("0;39"),      COL("0;39"),     COL("0;39"),
34    COL("0;32"),      COL("1;39"),     COL("1;39")};
35 #define FIELD_COLOR COL("34;1")
36 
37 static const char **colors = def_colors;
38 
39 int
jq_set_colors(const char * c)40 jq_set_colors(const char *c)
41 {
42   const char *e;
43   size_t i;
44 
45   if (c == NULL)
46     return 1;
47   colors = def_colors;
48   memset(color_bufs, 0, sizeof(color_bufs));
49   for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]); i++)
50     color_bufps[i] = def_colors[i];
51   for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]) && *c != '\0'; i++, c = e) {
52     if ((e = strchr(c, ':')) == NULL)
53       e = c + strlen(c);
54     if ((size_t)(e - c) > sizeof(color_bufs[i]) - 4 /* ESC [ m NUL */)
55       return 0;
56     color_bufs[i][0] = ESC[0];
57     color_bufs[i][1] = '[';
58     (void) strncpy(&color_bufs[i][2], c, e - c);
59     if (strspn(&color_bufs[i][2], "0123456789;") < strlen(&color_bufs[i][2]))
60       return 0;
61     color_bufs[i][2 + (e - c)] = 'm';
62     color_bufps[i] = color_bufs[i];
63     if (e[0] == ':')
64       e++;
65   }
66   colors = color_bufps;
67   return 1;
68 }
69 
put_buf(const char * s,int len,FILE * fout,jv * strout,int is_tty)70 static void put_buf(const char *s, int len, FILE *fout, jv *strout, int is_tty) {
71   if (strout) {
72     *strout = jv_string_append_buf(*strout, s, len);
73   } else {
74 #ifdef WIN32
75   /* See util.h */
76   if (is_tty) {
77     wchar_t *ws;
78     size_t wl;
79     if (len == -1)
80       len = strlen(s);
81     wl = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
82     ws = jv_mem_calloc((wl + 1), sizeof(*ws));
83     if (!ws)
84       return;
85     wl = MultiByteToWideChar(CP_UTF8, 0, s, len, ws, wl + 1);
86     ws[wl] = 0;
87     WriteConsoleW((HANDLE)_get_osfhandle(fileno(fout)), ws, wl, NULL, NULL);
88     free(ws);
89   } else
90     fwrite(s, 1, len, fout);
91 #else
92   fwrite(s, 1, len, fout);
93 #endif
94   }
95 }
96 
put_char(char c,FILE * fout,jv * strout,int T)97 static void put_char(char c, FILE* fout, jv* strout, int T) {
98   put_buf(&c, 1, fout, strout, T);
99 }
100 
put_str(const char * s,FILE * fout,jv * strout,int T)101 static void put_str(const char* s, FILE* fout, jv* strout, int T) {
102   put_buf(s, strlen(s), fout, strout, T);
103 }
104 
put_indent(int n,int flags,FILE * fout,jv * strout,int T)105 static void put_indent(int n, int flags, FILE* fout, jv* strout, int T) {
106   if (flags & JV_PRINT_TAB) {
107     while (n--)
108       put_char('\t', fout, strout, T);
109   } else {
110     n *= ((flags & (JV_PRINT_SPACE0 | JV_PRINT_SPACE1 | JV_PRINT_SPACE2)) >> 8);
111     while (n--)
112       put_char(' ', fout, strout, T);
113   }
114 }
115 
jvp_dump_string(jv str,int ascii_only,FILE * F,jv * S,int T)116 static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S, int T) {
117   assert(jv_get_kind(str) == JV_KIND_STRING);
118   const char* i = jv_string_value(str);
119   const char* end = i + jv_string_length_bytes(jv_copy(str));
120   const char* cstart;
121   int c = 0;
122   char buf[32];
123   put_char('"', F, S, T);
124   while ((i = jvp_utf8_next((cstart = i), end, &c))) {
125     assert(c != -1);
126     int unicode_escape = 0;
127     if (0x20 <= c && c <= 0x7E) {
128       // printable ASCII
129       if (c == '"' || c == '\\') {
130         put_char('\\', F, S, T);
131       }
132       put_char(c, F, S, T);
133     } else if (c < 0x20 || c == 0x7F) {
134       // ASCII control character
135       switch (c) {
136       case '\b':
137         put_char('\\', F, S, T);
138         put_char('b', F, S, T);
139         break;
140       case '\t':
141         put_char('\\', F, S, T);
142         put_char('t', F, S, T);
143         break;
144       case '\r':
145         put_char('\\', F, S, T);
146         put_char('r', F, S, T);
147         break;
148       case '\n':
149         put_char('\\', F, S, T);
150         put_char('n', F, S, T);
151         break;
152       case '\f':
153         put_char('\\', F, S, T);
154         put_char('f', F, S, T);
155         break;
156       default:
157         unicode_escape = 1;
158         break;
159       }
160     } else {
161       if (ascii_only) {
162         unicode_escape = 1;
163       } else {
164         put_buf(cstart, i - cstart, F, S, T);
165       }
166     }
167     if (unicode_escape) {
168       if (c <= 0xffff) {
169         snprintf(buf, sizeof(buf), "\\u%04x", c);
170       } else {
171         c -= 0x10000;
172         snprintf(buf, sizeof(buf), "\\u%04x\\u%04x",
173                 0xD800 | ((c & 0xffc00) >> 10),
174                 0xDC00 | (c & 0x003ff));
175       }
176       put_str(buf, F, S, T);
177     }
178   }
179   assert(c != -1);
180   put_char('"', F, S, T);
181 }
182 
put_refcnt(struct dtoa_context * C,int refcnt,FILE * F,jv * S,int T)183 static void put_refcnt(struct dtoa_context* C, int refcnt, FILE *F, jv* S, int T){
184   char buf[JVP_DTOA_FMT_MAX_LEN];
185   put_char(' ', F, S, T);
186   put_char('(', F, S, T);
187   put_str(jvp_dtoa_fmt(C, buf, refcnt), F, S, T);
188   put_char(')', F, S, T);
189 }
190 
jv_dump_term(struct dtoa_context * C,jv x,int flags,int indent,FILE * F,jv * S)191 static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FILE* F, jv* S) {
192   char buf[JVP_DTOA_FMT_MAX_LEN];
193   const char* color = 0;
194   double refcnt = (flags & JV_PRINT_REFCOUNT) ? jv_get_refcnt(x) - 1 : -1;
195   if (flags & JV_PRINT_COLOR) {
196     for (unsigned i=0; i<sizeof(color_kinds)/sizeof(color_kinds[0]); i++) {
197       if (jv_get_kind(x) == color_kinds[i]) {
198         color = colors[i];
199         put_str(color, F, S, flags & JV_PRINT_ISATTY);
200         break;
201       }
202     }
203   }
204   if (indent > MAX_PRINT_DEPTH) {
205     put_str("<skipped: too deep>", F, S, flags & JV_PRINT_ISATTY);
206   } else switch (jv_get_kind(x)) {
207   default:
208   case JV_KIND_INVALID:
209     if (flags & JV_PRINT_INVALID) {
210       jv msg = jv_invalid_get_msg(jv_copy(x));
211       if (jv_get_kind(msg) == JV_KIND_STRING) {
212         put_str("<invalid:", F, S, flags & JV_PRINT_ISATTY);
213         jvp_dump_string(msg, flags | JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);
214         put_str(">", F, S, flags & JV_PRINT_ISATTY);
215       } else {
216         put_str("<invalid>", F, S, flags & JV_PRINT_ISATTY);
217       }
218     } else {
219       assert(0 && "Invalid value");
220     }
221     break;
222   case JV_KIND_NULL:
223     put_str("null", F, S, flags & JV_PRINT_ISATTY);
224     break;
225   case JV_KIND_FALSE:
226     put_str("false", F, S, flags & JV_PRINT_ISATTY);
227     break;
228   case JV_KIND_TRUE:
229     put_str("true", F, S, flags & JV_PRINT_ISATTY);
230     break;
231   case JV_KIND_NUMBER: {
232     double d = jv_number_value(x);
233     if (d != d) {
234       // JSON doesn't have NaN, so we'll render it as "null"
235       put_str("null", F, S, flags & JV_PRINT_ISATTY);
236     } else {
237       // Normalise infinities to something we can print in valid JSON
238       if (d > DBL_MAX) d = DBL_MAX;
239       if (d < -DBL_MAX) d = -DBL_MAX;
240       put_str(jvp_dtoa_fmt(C, buf, d), F, S, flags & JV_PRINT_ISATTY);
241     }
242     break;
243   }
244   case JV_KIND_STRING:
245     jvp_dump_string(x, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);
246     if (flags & JV_PRINT_REFCOUNT)
247       put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);
248     break;
249   case JV_KIND_ARRAY: {
250     if (jv_array_length(jv_copy(x)) == 0) {
251       put_str("[]", F, S, flags & JV_PRINT_ISATTY);
252       break;
253     }
254     put_str("[", F, S, flags & JV_PRINT_ISATTY);
255     if (flags & JV_PRINT_PRETTY) {
256       put_char('\n', F, S, flags & JV_PRINT_ISATTY);
257       put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
258     }
259     jv_array_foreach(x, i, elem) {
260       if (i!=0) {
261         if (flags & JV_PRINT_PRETTY) {
262           put_str(",\n", F, S, flags & JV_PRINT_ISATTY);
263           put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
264         } else {
265           put_str(",", F, S, flags & JV_PRINT_ISATTY);
266         }
267       }
268       jv_dump_term(C, elem, flags, indent + 1, F, S);
269       if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);
270     }
271     if (flags & JV_PRINT_PRETTY) {
272       put_char('\n', F, S, flags & JV_PRINT_ISATTY);
273       put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);
274     }
275     if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);
276     put_char(']', F, S, flags & JV_PRINT_ISATTY);
277     if (flags & JV_PRINT_REFCOUNT)
278       put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);
279     break;
280   }
281   case JV_KIND_OBJECT: {
282     if (jv_object_length(jv_copy(x)) == 0) {
283       put_str("{}", F, S, flags & JV_PRINT_ISATTY);
284       break;
285     }
286     put_char('{', F, S, flags & JV_PRINT_ISATTY);
287     if (flags & JV_PRINT_PRETTY) {
288       put_char('\n', F, S, flags & JV_PRINT_ISATTY);
289       put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
290     }
291     int first = 1;
292     int i = 0;
293     jv keyset = jv_null();
294     while (1) {
295       jv key, value;
296       if (flags & JV_PRINT_SORTED) {
297         if (first) {
298           keyset = jv_keys(jv_copy(x));
299           i = 0;
300         } else {
301           i++;
302         }
303         if (i >= jv_array_length(jv_copy(keyset))) {
304           jv_free(keyset);
305           break;
306         }
307         key = jv_array_get(jv_copy(keyset), i);
308         value = jv_object_get(jv_copy(x), jv_copy(key));
309       } else {
310         if (first) {
311           i = jv_object_iter(x);
312         } else {
313           i = jv_object_iter_next(x, i);
314         }
315         if (!jv_object_iter_valid(x, i)) break;
316         key = jv_object_iter_key(x, i);
317         value = jv_object_iter_value(x, i);
318       }
319 
320       if (!first) {
321         if (flags & JV_PRINT_PRETTY){
322           put_str(",\n", F, S, flags & JV_PRINT_ISATTY);
323           put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
324         } else {
325           put_str(",", F, S, flags & JV_PRINT_ISATTY);
326         }
327       }
328       if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
329 
330       first = 0;
331       if (color) put_str(FIELD_COLOR, F, S, flags & JV_PRINT_ISATTY);
332       jvp_dump_string(key, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);
333       jv_free(key);
334       if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
335 
336       if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);
337       put_str((flags & JV_PRINT_PRETTY) ? ": " : ":", F, S, flags & JV_PRINT_ISATTY);
338       if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
339 
340       jv_dump_term(C, value, flags, indent + 1, F, S);
341       if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);
342     }
343     if (flags & JV_PRINT_PRETTY) {
344       put_char('\n', F, S, flags & JV_PRINT_ISATTY);
345       put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);
346     }
347     if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);
348     put_char('}', F, S, flags & JV_PRINT_ISATTY);
349     if (flags & JV_PRINT_REFCOUNT)
350       put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);
351   }
352   }
353   jv_free(x);
354   if (color) {
355     put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
356   }
357 }
358 
jv_dumpf(jv x,FILE * f,int flags)359 void jv_dumpf(jv x, FILE *f, int flags) {
360   struct dtoa_context C;
361   jvp_dtoa_context_init(&C);
362   jv_dump_term(&C, x, flags, 0, f, 0);
363   jvp_dtoa_context_free(&C);
364 }
365 
jv_dump(jv x,int flags)366 void jv_dump(jv x, int flags) {
367   jv_dumpf(x, stdout, flags);
368 }
369 
370 /* This one is nice for use in debuggers */
jv_show(jv x,int flags)371 void jv_show(jv x, int flags) {
372   if (flags == -1)
373     flags = JV_PRINT_PRETTY | JV_PRINT_COLOR | JV_PRINT_INDENT_FLAGS(2);
374   jv_dumpf(jv_copy(x), stderr, flags | JV_PRINT_INVALID);
375   fflush(stderr);
376 }
377 
jv_dump_string(jv x,int flags)378 jv jv_dump_string(jv x, int flags) {
379   struct dtoa_context C;
380   jvp_dtoa_context_init(&C);
381   jv s = jv_string("");
382   jv_dump_term(&C, x, flags, 0, 0, &s);
383   jvp_dtoa_context_free(&C);
384   return s;
385 }
386 
jv_dump_string_trunc(jv x,char * outbuf,size_t bufsize)387 char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) {
388   x = jv_dump_string(x,0);
389   const char* p = jv_string_value(x);
390   const size_t len = strlen(p);
391   strncpy(outbuf, p, bufsize);
392   outbuf[bufsize - 1] = 0;
393   if (len > bufsize - 1 && bufsize >= 4) {
394     // Indicate truncation with '...'
395     outbuf[bufsize - 2]='.';
396     outbuf[bufsize - 3]='.';
397     outbuf[bufsize - 4]='.';
398   }
399   jv_free(x);
400   return outbuf;
401 }
402