diff options
Diffstat (limited to 'src/builtin.c')
-rw-r--r-- | src/builtin.c | 64 |
1 files changed, 62 insertions, 2 deletions
diff --git a/src/builtin.c b/src/builtin.c index aa0ab4d5..24e311e1 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -396,6 +396,24 @@ static jv f_utf8bytelength(jq_state *jq, jv input) { #define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +static const unsigned char BASE64_ENCODE_TABLE[64 + 1] = CHARS_ALPHANUM "+/"; +static const unsigned char BASE64_INVALID_ENTRY = 0xFF; +static const unsigned char BASE64_DECODE_TABLE[255] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 62, // + + 0xFF, 0xFF, 0xFF, + 63, // / + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9 + 0xFF, 0xFF, 0xFF, + 99, // = + 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // A-Z + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // a-z + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + + static jv escape_string(jv input, const char* escapings) { assert(jv_get_kind(input) == JV_KIND_STRING); @@ -548,7 +566,6 @@ static jv f_format(jq_state *jq, jv input, jv fmt) { jv_free(fmt); input = f_tostring(jq, input); jv line = jv_string(""); - const char b64[64 + 1] = CHARS_ALPHANUM "+/"; const unsigned char* data = (const unsigned char*)jv_string_value(input); int len = jv_string_length_bytes(jv_copy(input)); for (int i=0; i<len; i+=3) { @@ -560,7 +577,7 @@ static jv f_format(jq_state *jq, jv input, jv fmt) { } char buf[4]; for (int j=0; j<4; j++) { - buf[j] = b64[(code >> (18 - j*6)) & 0x3f]; + buf[j] = BASE64_ENCODE_TABLE[(code >> (18 - j*6)) & 0x3f]; } if (n < 3) buf[3] = '='; if (n < 2) buf[2] = '='; @@ -568,6 +585,49 @@ static jv f_format(jq_state *jq, jv input, jv fmt) { } jv_free(input); return line; + } else if (!strcmp(fmt_s, "base64d")) { + jv_free(fmt); + input = f_tostring(jq, input); + const unsigned char* data = (const unsigned char*)jv_string_value(input); + int len = jv_string_length_bytes(jv_copy(input)); + size_t decoded_len = (3 * len) / 4; // 3 usable bytes for every 4 bytes of input + char *result = malloc(decoded_len * sizeof(char)); + memset(result, 0, decoded_len * sizeof(char)); + uint32_t ri = 0; + int input_bytes_read=0; + uint32_t code = 0; + for (int i=0; i<len && data[i] != '='; i++) { + if (BASE64_DECODE_TABLE[data[i]] == BASE64_INVALID_ENTRY) { + free(result); + return type_error(input, "is not valid base64 data"); + } + + code <<= 6; + code |= BASE64_DECODE_TABLE[data[i]]; + input_bytes_read++; + + if (input_bytes_read == 4) { + result[ri++] = (code >> 16) & 0xFF; + result[ri++] = (code >> 8) & 0xFF; + result[ri++] = code & 0xFF; + input_bytes_read = 0; + code = 0; + } + } + if (input_bytes_read == 3) { + result[ri++] = (code >> 10) & 0xFF; + result[ri++] = (code >> 2) & 0xFF; + } else if (input_bytes_read == 2) { + result[ri++] = (code >> 4) & 0xFF; + } else if (input_bytes_read == 1) { + free(result); + return type_error(input, "trailing base64 byte found"); + } + + jv line = jv_string_sized(result, ri); + jv_free(input); + free(result); + return line; } else { jv_free(input); return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format"))); |