summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2021-06-07 09:03:25 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2021-06-07 09:03:25 +0900
commit0afa651363c0b22f238c7442e84926e4e3f10574 (patch)
tree64c5ab2a87c452cc0b0b83c954eb22f1990cb8a2
parentbba044cfc8cbd8eb1d2da9db2298a80905e125bd (diff)
downloadmruby-0afa651363c0b22f238c7442e84926e4e3f10574.tar.gz
mruby-0afa651363c0b22f238c7442e84926e4e3f10574.zip
pack.c: support `M` specifier (quoted-printable).
-rw-r--r--mrbgems/mruby-pack/src/pack.c112
-rw-r--r--mrbgems/mruby-pack/test/pack.rb11
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"]