diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2021-06-07 09:03:25 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2021-06-07 09:03:25 +0900 |
| commit | 0afa651363c0b22f238c7442e84926e4e3f10574 (patch) | |
| tree | 64c5ab2a87c452cc0b0b83c954eb22f1990cb8a2 | |
| parent | bba044cfc8cbd8eb1d2da9db2298a80905e125bd (diff) | |
| download | mruby-0afa651363c0b22f238c7442e84926e4e3f10574.tar.gz mruby-0afa651363c0b22f238c7442e84926e4e3f10574.zip | |
pack.c: support `M` specifier (quoted-printable).
| -rw-r--r-- | mrbgems/mruby-pack/src/pack.c | 112 | ||||
| -rw-r--r-- | mrbgems/mruby-pack/test/pack.rb | 11 |
2 files changed, 121 insertions, 2 deletions
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c index 05d5d0875..c8e0746d5 100644 --- a/mrbgems/mruby-pack/src/pack.c +++ b/mrbgems/mruby-pack/src/pack.c @@ -37,6 +37,7 @@ enum pack_dir { PACK_DIR_STR, /* A */ PACK_DIR_HEX, /* h */ PACK_DIR_BASE64, /* m */ + PACK_DIR_QENC, /* M */ PACK_DIR_NUL, /* x */ PACK_DIR_INVALID }; @@ -76,7 +77,7 @@ hex2int(unsigned char ch) else if (ch >= 'a' && ch <= 'f') return 10 + (ch - 'a'); else - return 0; + return -1; } static void @@ -724,10 +725,12 @@ pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, u a = b = 0; if (slen > 0) { a = hex2int(*sptr++); + if (a < 0) break; slen--; } if (slen > 0) { b = hex2int(*sptr++); + if (b < 0) break; slen--; } *dptr++ = (a << ashift) + (b << bshift); @@ -907,6 +910,100 @@ done: } static int +pack_M(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags) +{ + static const char hex_table[] = "0123456789ABCDEF"; + char buff[1024]; + char *s = RSTRING_PTR(src); + char *send = s + RSTRING_LEN(src); + int i = 0, n = 0, prev = EOF; + int dlen = 0; + + if (count <= 1) count = 72; + while (s < send) { + if ((*s > 126) || + (*s < 32 && *s != '\n' && *s != '\t') || + (*s == '=')) { + buff[i++] = '='; + buff[i++] = hex_table[(*s & 0xf0) >> 4]; + buff[i++] = hex_table[*s & 0x0f]; + n += 3; + prev = EOF; + } + else if (*s == '\n') { + if (prev == ' ' || prev == '\t') { + buff[i++] = '='; + buff[i++] = *s; + } + buff[i++] = *s; + n = 0; + prev = *s; + } + else { + buff[i++] = *s; + n++; + prev = *s; + } + if (n > count) { + buff[i++] = '='; + buff[i++] = '\n'; + n = 0; + prev = '\n'; + } + if (i > 1024 - 5) { + str_len_ensure(mrb, dst, didx+i); + memcpy(RSTRING_PTR(dst), buff, i); + dlen += i; + i = 0; + } + s++; + } + if (n > 0) { + buff[i++] = '='; + buff[i++] = '\n'; + } + if (i > 0) { + str_len_ensure(mrb, dst, didx+i); + memcpy(RSTRING_PTR(dst), buff, i); + dlen += i; + } + return dlen; +} + +static int +unpack_M(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags) +{ + mrb_value buf = mrb_str_new(mrb, 0, slen); + const char *s = src, *ss = s; + const char *send = src + slen; + char *ptr = RSTRING_PTR(buf); + int c1, c2; + + while (s < send) { + if (*s == '=') { + if (++s == send) break; + if (s+1 < send && *s == '\r' && *(s+1) == '\n') + s++; + if (*s != '\n') { + if ((c1 = hex2int(*s)) == -1) break; + if (++s == send) break; + if ((c2 = hex2int(*s)) == -1) break; + *ptr++ = (char)(c1 << 4 | c2); + } + } + else { + *ptr++ = *s; + } + s++; + ss = s; + } + buf = mrb_str_resize(mrb, buf, (mrb_int)(ptr - RSTRING_PTR(buf))); + mrb_str_cat(mrb, buf, ss, send-ss); + mrb_ary_push(mrb, ary, buf); + return slen; +} + +static int pack_x(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags) { long i; @@ -1060,6 +1157,11 @@ alias: type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2; break; + case 'M': + dir = PACK_DIR_QENC; + type = PACK_TYPE_STRING; + flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2; + break; case 'N': /* = "L>" */ dir = PACK_DIR_LONG; type = PACK_TYPE_INTEGER; @@ -1120,7 +1222,7 @@ alias: flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z; break; case 'p': case 'P': - case 'M': case 'X': + case 'X': case '%': case '@': mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c is not supported", (char)t); break; @@ -1242,6 +1344,9 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary) case PACK_DIR_BASE64: ridx += pack_m(mrb, o, result, ridx, count, flags); break; + case PACK_DIR_QENC: + ridx += pack_M(mrb, o, result, ridx, count, flags); + break; case PACK_DIR_HEX: ridx += pack_h(mrb, o, result, ridx, count, flags); break; @@ -1322,6 +1427,9 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single) case PACK_DIR_BASE64: srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags); break; + case PACK_DIR_QENC: + srcidx += unpack_M(mrb, sptr, srclen - srcidx, result, flags); + break; } continue; } diff --git a/mrbgems/mruby-pack/test/pack.rb b/mrbgems/mruby-pack/test/pack.rb index 93f5d8d46..56f63a3d7 100644 --- a/mrbgems/mruby-pack/test/pack.rb +++ b/mrbgems/mruby-pack/test/pack.rb @@ -28,6 +28,17 @@ assert('pack("m")') do assert_pack "m0", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"] end +# pack & unpack 'M' (Quoted-printable) +assert('pack("M")') do + assert_pack "M", "123=\n", ["123"] + assert_pack "M", "=3D\n", ["=\n"] + assert_pack "M", "=E3=81=82=\n", ["あ"] + + assert_equal ["123"], "123=\n".unpack("M") + assert_equal ["=\n"], "=3D\n".unpack("M") + assert_equal ["あ"], "=E3=81=82=\n".unpack("M") +end + # pack & unpack 'H' assert('pack("H")') do assert_pack "H*", "01", ["3031"] |
