diff options
| -rw-r--r-- | mrbgems/mruby-pack/src/pack.c | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c index 30b5e7fa0..70852438a 100644 --- a/mrbgems/mruby-pack/src/pack.c +++ b/mrbgems/mruby-pack/src/pack.c @@ -39,6 +39,7 @@ enum pack_dir { PACK_DIR_BASE64, /* m */ PACK_DIR_QENC, /* M */ PACK_DIR_NUL, /* x */ + PACK_DIR_BACK, /* X */ PACK_DIR_INVALID }; @@ -1024,6 +1025,24 @@ unpack_x(mrb_state *mrb, int slen, int count) return count; } +static int +pack_X(mrb_state *mrb, mrb_value dst, mrb_int didx, int count) +{ + if (count > didx) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "X outside of string"); + } + return count; +} + +static int +unpack_X(mrb_state *mrb, int sidx, int count) +{ + if (sidx < count) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "X outside of string"); + } + return count; +} + static void prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl) { @@ -1214,13 +1233,16 @@ alias: dir = PACK_DIR_NUL; type = PACK_TYPE_NONE; break; + case 'X': + dir = PACK_DIR_BACK; + type = PACK_TYPE_NONE; + break; case 'Z': dir = PACK_DIR_STR; type = PACK_TYPE_STRING; flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z; break; case 'p': case 'P': - case 'X': case '%': case '@': mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c is not supported", (char)t); break; @@ -1300,6 +1322,11 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary) ridx += pack_x(mrb, result, ridx, count); continue; } + else if (dir == PACK_DIR_BACK) { + if (count > 0 && ridx > INT_MAX - count) goto overflow; + ridx -= pack_X(mrb, result, ridx, count); + continue; + } if ((flags & PACK_FLAG_WIDTH) && aidx >= RARRAY_LEN(ary)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); @@ -1412,6 +1439,10 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single) srcidx += unpack_x(mrb, srclen-srcidx, count); continue; } + else if (dir == PACK_DIR_BACK) { + srcidx -= unpack_X(mrb, srcidx, count); + continue; + } if (flags & PACK_FLAG_COUNT2) { sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx; |
