I am writing simple formatter for valid JSON. Is reads JSON data from stdin and writes formatted output to stdout.
Goals:
- given valid input produce valid formatted JSON output
- constant memory usage
- minimal viable program (smallest program that solves a particular problem)
- feature free
- easy to read and understand
- be C99 compatible
Non-goals:
- validating JSON
- handling arguments
I am not aiming JSON validation right now (as said "formatter for valid JSON").
I tried adding arguments (like setting placeholder string with -p
) but faced several issues:
- unclear what arguments must be implemented
- I don't feel comfortable yet writing argument parsing code.
So I skipped these for now.
Latest file in GitHub: https://github.com/sineemore/juu/blob/master/juu.c
#include <stdio.h>
#include <stdlib.h> /* EXIT_SUCCESS */
#define BUF_SIZE (1024 * 4)
int main(int argc, char **argv) {
char buf[BUF_SIZE];
const char placeholder[] = " ";
unsigned int indent = 0;
unsigned int i = 0;
unsigned int k = 0;
char is_string = 0;
char escaped = 0;
char ch;
size_t n;
while (0 < (n = fread(&buf, sizeof(char), BUF_SIZE, stdin))) {
for (k = 0; k < n; k++) {
ch = buf[k];
if (is_string) {
/* Inside quoted string */
putchar(ch);
if (! escaped) {
if (ch == '"') {
/* Unescaped quote, string just ended */
is_string = 0;
} else if (ch == '\\') {
escaped = 1;
}
} else {
escaped = 0;
}
continue;
}
switch (ch) {
case ' ':
case '\t':
case '\n':
case '\r':
/* Ignoring original formatting */
break;
case '{':
case '[':
putchar(ch);
putchar('\n');
i = ++indent;
while (i-- > 0) fputs(placeholder, stdout);
break;
case '}':
case ']':
putchar('\n');
i = --indent;
while (i-- > 0) fputs(placeholder, stdout);
putchar(ch);
if (indent == 0) putchar('\n');
break;
case ',':
putchar(',');
putchar('\n');
i = indent;
while (i-- > 0) fputs(placeholder, stdout);
break;
case ':':
putchar(':');
putchar(' ');
break;
case '"':
/* String/property key start, see if clause on top (line 20) */
putchar('"');
is_string = 1;
break;
default:
/* Numbers, true, false, null */
putchar(ch);
break;
}
}
}
return EXIT_SUCCESS;
}
Example output:
$ wget -qO- 'https://xkcd.com/info.0.json' | juu
{
"month": "4",
"num": 1979,
"link": "",
"year": "2018",
"news": "",
"safe_title": "History",
"transcript": "",
"alt": "HISTORIANS: We've decided to trim the past down to make things more manageable. Using BCE/CE, would you rather we lose the odd-numbered or even-numbered years?",
"img": "https://imgs.xkcd.com/comics/history.png",
"title": "History",
"day": "11"
}
UPDATE:
- I missed ALL
ferror()
calls, fixing this part. - Also program doesn't handle
SIGPIPE
, therefore it may be killed. Just tested it. I don't see a clear solution, should I setSIG_IGN
?