json2tsv.c - json2tsv - JSON to TSV converter
(HTM) git clone git://git.codemadness.org/json2tsv
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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 }