bson2json/bson2json.c

247 lines
5.4 KiB
C

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#define DATABUF_LEN 16
char DATA_BUFFER[DATABUF_LEN]; // data buffer for funny casts
void
die(char *format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
/* this function performs required json escaping from stdin
* and prints out the correct stuff
*/
void
json_string_stdin(int32_t *doc_size)
{
int c;
putchar('"');
for (c = getchar(); c != 0x00 && c != EOF; c = getchar()) {
*doc_size -= 1;
switch (c) {
case '"':
fputs("\\\"", stdout);
break;
case '\\':
fputs("\\\\", stdout);
break;
case '/':
fputs("\\/", stdout);
break;
case '\b':
fputs("\\b", stdout);
break;
case '\f':
fputs("\\f", stdout);
break;
case '\n':
fputs("\\n", stdout);
break;
case '\r':
fputs("\\r", stdout);
break;
case '\t':
fputs("\\t", stdout);
break;
default:
if (c <= 0x1F) {
fprintf(stderr, "warning: invalid control character %x in string, ignoring\n", c);
continue;
}
putchar(c);
}
}
putchar('"');
*doc_size -= 1;
}
/* this function simply discards stdin while removing size
* this is for array keys in an array
*/
void
json_trash_string_stdin(int32_t *doc_size)
{
while (getchar() != 0x00) {
*doc_size -= 1;
}
*doc_size -= 1;
}
// prints bson document as json, returns complete size of the document
int32_t
print_bson(uint8_t is_array)
{
int32_t size;
int32_t size_ret;
int c;
fread(&size, 4, 1, stdin);
size_ret = size;
size -= 4;
if (is_array) {
putchar('[');
} else {
putchar('{');
}
while (1) {
c = getchar();
size -= 1;
if (c == 0x00) {
if (size == 0) {
if (is_array) {
putchar(']');
} else {
putchar('}');
}
return size_ret;
}
die("bson2json: error: 0x00 byte reached prematurely! check your data.\n");
}
if (is_array) {
json_trash_string_stdin(&size);
} else {
json_string_stdin(&size);
putchar(':');
}
switch (c) {
case 0x01: // double
fread(&DATA_BUFFER, 8, 1, stdin);
printf("%.19f", *((double*)DATA_BUFFER));
size -= 8;
break;
case 0x02: case 0x0D: case 0x0E: // string, javascript code, symbol
fread(&DATA_BUFFER, 4, 1, stdin);
size -= 4;
json_string_stdin(&size);
break;
case 0x03: // document
size -= print_bson(0);
break;
case 0x04: // array
size -= print_bson(1);
break;
case 0x05: // binary
fread(&DATA_BUFFER, 4, 1, stdin);
getchar(); // just throw the next char away
size -= 5 + *((int32_t*)DATA_BUFFER);
putchar('"');
while (*((int32_t*)DATA_BUFFER) > 0) {
c = getchar();
printf("%02x", c);
*((int32_t*)DATA_BUFFER) -= 1;
}
putchar('"');
break;
case 0x06: // undefined (deprecated)
fputs("null", stdout);
break;
case 0x07: // objectid
fread(&DATA_BUFFER, 4, 1, stdin);
printf("{\"$ObjectId:timestamp\":%"PRIu32",", *((uint32_t*)DATA_BUFFER));
memset(DATA_BUFFER, 0, 8);
fread(&DATA_BUFFER, 5, 1, stdin);
printf("\"$ObjectId:rand\":%"PRIu64",", *((uint64_t*)DATA_BUFFER));
memset(DATA_BUFFER, 0, 4);
fread(&DATA_BUFFER, 3, 1, stdin);
printf("\"$ObjectId:counter\":%"PRIu32"}", *((uint32_t*)DATA_BUFFER));
size -= 12;
break;
case 0x08: // boolean
fread(&DATA_BUFFER, 1, 1, stdin);
if (*DATA_BUFFER == 0x00) {
fputs("false", stdout);
} else {
// technically it should only be true if it == 0x01
// and invalid otherwise but idc
fputs("true", stdout);
}
size -= 1;
break;
case 0x09: case 0x12: // 64 bit ints (utc datetime and int64)
fread(&DATA_BUFFER, 8, 1, stdin);
printf("%"PRId64, *((int64_t*)DATA_BUFFER));
size -= 8;
break;
case 0x0A: // null
fputs("null", stdout);
break;
case 0x0B: // regex
fputs("{\"$regex:pattern\":", stdout);
json_string_stdin(&size);
fputs(",\"$regex:options\":", stdout);
json_string_stdin(&size);
putchar('}');
break;
case 0x0C: // dbpointer
fread(&DATA_BUFFER, 4, 1, stdin);
fputs("{\"$DBPointer:string\":", stdout);
json_string_stdin(&size);
memset(&DATA_BUFFER, 0, 16);
fread(&DATA_BUFFER, 12, 1, stdin);
printf(",\"$DBPointer:pointer\":%"PRIu64"}", *((uint64_t*)DATA_BUFFER));
size -= 16;
break;
case 0x0F: // code with scope
fread(&DATA_BUFFER, 4, 1, stdin); // entire
fread(&DATA_BUFFER, 4, 1, stdin); // string
fputs("{\"$code_w_s:code\":", stdout);
json_string_stdin(&size);
fputs(",\"$code_w_s:scope\":", stdout);
size -= 8 + print_bson(0);
putchar('}');
break;
case 0x10: // 32 bit int
fread(&DATA_BUFFER, 4, 1, stdin);
printf("%"PRId32, *((int32_t*)DATA_BUFFER));
size -= 4;
break;
case 0x11: // timestamp (uint64)
fread(&DATA_BUFFER, 8, 1, stdin);
printf("%"PRId64, *((uint64_t*)DATA_BUFFER));
size -= 8;
break;
case 0x13: // 128-bit decimal
fputs("warning: decimal128 number in input. these are a binary string until c23 support is better\n", stderr);
putchar('"');
for (uint8_t i = 0; i < 16; i++) {
printf("%02x", getchar());
}
putchar('"');
size -= 16;
break;
case 0xFF: case 0x7F: // min/max type idc about these
fputs("null", stdout);
break;
default:
die("bson2json: error: unknown byte for item type. this is most likely the result of some other data bug.\n");
}
if (size > 1) {
putchar(',');
}
}
}
int
main(void)
{
print_bson(0);
printf("\n");
return 0;
}