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 }