247 lines
5.4 KiB
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;
|
|
}
|