#include #include #include #include #include #include #include #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; }