|
|
json2tsv.c - json2tsv - JSON to TSV converter |
|
|
 |
git clone git://git.codemadness.org/json2tsv (git://git.codemadness.org) |
|
|
 |
Log |
|
|
 |
Files |
|
|
 |
Refs |
|
|
 |
README |
|
|
 |
LICENSE |
|
|
|
--- |
|
|
|
json2tsv.c (4701B) |
|
|
|
--- |
|
|
|
1 #include <errno.h> |
|
|
|
2 #include <limits.h> |
|
|
|
3 #include <stdint.h> |
|
|
|
4 #include <stdio.h> |
|
|
|
5 #include <stdlib.h> |
|
|
|
6 #include <string.h> |
|
|
|
7 |
|
|
|
8 #ifdef __OpenBSD__ |
|
|
|
9 #include <unistd.h> |
|
|
|
10 #else |
|
|
|
11 #define pledge(a,b) 0 |
|
|
|
12 #endif |
|
|
|
13 |
|
|
|
14 #include "json.h" |
|
|
|
15 |
|
|
|
16 /* ctype-like macros, but always compatible with ASCII / UTF-8 */ |
|
|
|
17 #define ISDIGIT(c) (((unsigned)c) - '0' < 10) |
|
|
|
18 #define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f) |
|
|
|
19 |
|
|
|
20 static int nflag = 0; /* -n flag: show indices count for arrays */ |
|
|
|
21 static int rflag = 0; /* -r flag: show all control-characters */ |
|
|
|
22 static int uflag = 0; /* -u flag: flush output after printing each value */ |
|
|
|
23 static int fs = '\t', rs = '\n'; |
|
|
|
24 |
|
|
|
25 static void (*printvalue)(const char *, size_t); |
|
|
|
26 |
|
|
|
27 void |
|
|
|
28 tsv_printvalue(const char *s, size_t len) |
|
|
|
29 { |
|
|
|
30 const char *e; |
|
|
|
31 |
|
|
|
32 e = s + len; |
|
|
|
33 for (; s != e; s++) { |
|
|
|
34 /* escape some chars */ |
|
|
|
35 switch (*s) { |
|
|
|
36 case '\n': putchar('\\'); putchar('n'); break; |
|
|
|
37 case '\\': putchar('\\'); putchar('\\'); break; |
|
|
|
38 case '\t': putchar('\\'); putchar('t'); break; |
|
|
|
39 default: |
|
|
|
40 /* ignore other control chars */ |
|
|
|
41 if (!rflag && ISCNTRL((unsigned char)*s)) |
|
|
|
42 continue; |
|
|
|
43 putchar(*s); |
|
|
|
44 } |
|
|
|
45 } |
|
|
|
46 } |
|
|
|
47 |
|
|
|
48 void |
|
|
|
49 rs_printvalue(const char *s, size_t len) |
|
|
|
50 { |
|
|
|
51 const char *e; |
|
|
|
52 |
|
|
|
53 e = s + len; |
|
|
|
54 for (; s != e; s++) { |
|
|
|
55 if (*s == fs || *s == rs) |
|
|
|
56 continue; |
|
|
|
57 |
|
|
|
58 switch (*s) { |
|
|
|
59 case '\n': |
|
|
|
60 case '\t': |
|
|
|
61 putchar(*s); |
|
|
|
62 break; |
|
|
|
63 default: |
|
|
|
64 /* ignore other control chars */ |
|
|
|
65 if (!rflag && ISCNTRL((unsigned char)*s)) |
|
|
|
66 continue; |
|
|
|
67 putchar(*s); |
|
|
|
68 } |
|
|
|
69 } |
|
|
|
70 } |
|
|
|
71 |
|
|
|
72 /* optimized printing an unsigned number (compared to printf("%zu")) */ |
|
|
|
73 void |
|
|
|
74 printnum(uintmax_t x) |
|
|
|
75 { |
|
|
|
76 char buf[64], *s, *e; |
|
|
|
77 unsigned long y; |
|
|
|
78 |
|
|
|
79 if (!x) { |
|
|
|
80 putchar('0'); |
|
|
|
81 return; |
|
|
|
82 } |
|
|
|
83 |
|
|
|
84 s = e = buf + sizeof(buf) - 1; |
|
|
|
85 |
|
|
|
86 for (; x > ULONG_MAX; x /= 10) |
|
|
|
87 *--s = '0' + x % 10; |
|
|
|
88 for (y = x; y; y /= 10) |
|
|
|
89 *--s = '0' + y % 10; |
|
|
|
90 |
|
|
|
91 for (; s < e; s++) |
|
|
|
92 putchar(*s); |
|
|
|
93 } |
|
|
|
94 |
|
|
|
95 void |
|
|
|
96 processnode(struct json_node *nodes, size_t depth, const char *value, size_t valuelen) |
|
|
|
97 { |
|
|
|
98 size_t i; |
|
|
|
99 |
|
|
|
100 for (i = 0; i < depth; i++) { |
|
|
|
101 printvalue(nodes[i].name, strlen(nodes[i].name)); |
|
|
|
102 |
|
|
|
103 if (i + 1 == depth && |
|
|
|
104 (nodes[i].type == JSON_TYPE_OBJECT || |
|
|
|
105 nodes[i].type == JSON_TYPE_ARRAY)) |
|
|
|
106 continue; |
|
|
|
107 |
|
|
|
108 if (nodes[i].type == JSON_TYPE_OBJECT) { |
|
|
|
109 putchar('.'); |
|
|
|
110 } else if (nodes[i].type == JSON_TYPE_ARRAY) { |
|
|
|
111 putchar('['); |
|
|
|
112 if (nflag) |
|
|
|
113 printnum(nodes[i].index); |
|
|
|
114 putchar(']'); |
|
|
|
115 } |
|
|
|
116 } |
|
|
|
117 |
|
|
|
118 putchar(fs); |
|
|
|
119 putchar(nodes[depth - 1].type); |
|
|
|
120 putchar(fs); |
|
|
|
121 printvalue(value, valuelen); |
|
|
|
122 putchar(rs); |
|
|
|
123 |
|
|
|
124 if ((uflag && fflush(stdout)) || ferror(stdout)) { |
|
|
|
125 fprintf(stderr, "write error: <stdout>\n"); |
|
|
|
126 exit(2); |
|
|
|
127 } |
|
|
|
128 } |
|
|
|
129 |
|
|
|
130 int |
|
|
|
131 readnum(const char *s, int base) |
|
|
|
132 { |
|
|
|
133 long l; |
|
|
|
134 char *end; |
|
|
|
135 |
|
|
|
136 errno = 0; |
|
|
|
137 l = strtol(s, &end, base); |
|
|
|
138 if (errno || s == end || *end != '\0' || l < 0 || l > 255) { |
|
|
|
139 fprintf(stderr, "invalid number\n"); |
|
|
|
140 exit(3); |
|
|
|
141 } |
|
|
|
142 |
|
|
|
143 return (int)l; |
|
|
|
144 } |
|
|
|
145 |
|
|
|
146 int |
|
|
|
147 readchar(const char *s) |
|
|
|
148 { |
|
|
|
149 if (!*s) { |
|
|
|
150 fprintf(stderr, "invalid character\n"); |
|
|
|
151 exit(3); |
|
|
|
152 } else if (strlen(s) == 1) { |
|
|
|
153 return *s; |
|
|
|
154 } else if (*s == '\\') { |
|
|
|
155 s++; |
|
|
|
156 if (*s == 'x') |
|
|
|
157 return readnum(++s, 16); /* hexadecimal */ |
|
|
|
158 else if (ISDIGIT((unsigned char)*s)) |
|
|
|
159 return readnum(s, 8); /* octal */ |
|
|
|
160 |
|
|
|
161 if (*(s + 1)) { |
|
|
|
162 fprintf(stderr, "unsupported format\n"); |
|
|
|
163 exit(3); |
|
|
|
164 } |
|
|
|
165 switch (*s) { |
|
|
|
166 case '\\': return '\\'; |
|
|
|
167 case 't': return '\t'; |
|
|
|
168 case 'n': return '\n'; |
|
|
|
169 case 'r': return '\r'; |
|
|
|
170 default: |
|
|
|
171 fprintf(stderr, "unsupported escape character\n"); |
|
|
|
172 exit(3); |
|
|
|
173 } |
|
|
|
174 } |
|
|
|
175 /* base 0 (decimal, octal, hex) using strtol() format */ |
|
|
|
176 return readnum(s, 0); |
|
|
|
177 } |
|
|
|
178 |
|
|
|
179 void |
|
|
|
180 usage(const char *argv0) |
|
|
|
181 { |
|
|
|
182 fprintf(stderr, "usage: %s [-n] [-r] [-u] [-F fs] [-R rs]\n", argv0); |
|
|
|
183 exit(3); |
|
|
|
184 } |
|
|
|
185 |
|
|
|
186 int |
|
|
|
187 main(int argc, char *argv[]) |
|
|
|
188 { |
|
|
|
189 int i, j; |
|
|
|
190 |
|
|
|
191 if (pledge("stdio", NULL) == -1) { |
|
|
|
192 fprintf(stderr, "pledge stdio: %s\n", strerror(errno)); |
|
|
|
193 return 1; |
|
|
|
194 } |
|
|
|
195 |
|
|
|
196 printvalue = tsv_printvalue; |
|
|
|
197 for (i = 1; i < argc; i++) { |
|
|
|
198 for (j = 1; i < argc && argv[i][j]; j++) { |
|
|
|
199 switch (argv[i][j]) { |
|
|
|
200 case 'n': |
|
|
|
201 nflag = 1; |
|
|
|
202 break; |
|
|
|
203 case 'r': |
|
|
|
204 rflag = 1; |
|
|
|
205 break; |
|
|
|
206 case 'u': |
|
|
|
207 uflag = 1; |
|
|
|
208 break; |
|
|
|
209 case 'F': |
|
|
|
210 if (i + 1 >= argc) |
|
|
|
211 usage(argv[0]); |
|
|
|
212 fs = readchar(argv[++i]); |
|
|
|
213 printvalue = rs_printvalue; |
|
|
|
214 goto nextarg; |
|
|
|
215 case 'R': |
|
|
|
216 if (i + 1 >= argc) |
|
|
|
217 usage(argv[0]); |
|
|
|
218 rs = readchar(argv[++i]); |
|
|
|
219 printvalue = rs_printvalue; |
|
|
|
220 goto nextarg; |
|
|
|
221 default: |
|
|
|
222 usage(argv[0]); |
|
|
|
223 break; |
|
|
|
224 } |
|
|
|
225 } |
|
|
|
226 nextarg:; |
|
|
|
227 } |
|
|
|
228 |
|
|
|
229 switch (parsejson(processnode)) { |
|
|
|
230 case JSON_ERROR_MEM: |
|
|
|
231 fputs("error: cannot allocate enough memory\n", stderr); |
|
|
|
232 return 2; |
|
|
|
233 case JSON_ERROR_INVALID: |
|
|
|
234 fputs("error: invalid JSON\n", stderr); |
|
|
|
235 return 1; |
|
|
|
236 } |
|
|
|
237 |
|
|
|
238 if (ferror(stdin)) { |
|
|
|
239 fprintf(stderr, "read error: <stdin>\n"); |
|
|
|
240 return 2; |
|
|
|
241 } |
|
|
|
242 if (fflush(stdout) || ferror(stdout)) { |
|
|
|
243 fprintf(stderr, "write error: <stdout>\n"); |
|
|
|
244 return 2; |
|
|
|
245 } |
|
|
|
246 |
|
|
|
247 return 0; |
|
|
|
248 } |
|