From: Arran Cudbard-Bell Date: Mon, 27 Jul 2020 16:44:25 +0000 (-0400) Subject: sbuff: Add the ability to process escape character sequences like \x and... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=22d5c50cfa2ad653f75feab47b894ccfabdb2b47;p=thirdparty%2Ffreeradius-server.git sbuff: Add the ability to process escape character sequences like \x and \000 --- diff --git a/src/lib/util/sbuff.c b/src/lib/util/sbuff.c index f96a3c6acbe..1874cc69c28 100644 --- a/src/lib/util/sbuff.c +++ b/src/lib/util/sbuff.c @@ -299,8 +299,7 @@ int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len) */ #define FILL_OR_GOTO_DONE(_out, _in, _len) \ do { \ - ssize_t _copied; \ - _copied = fr_sbuff_in_bstrncpy(_out, fr_sbuff_current(_in), _len); \ + ssize_t _copied = fr_sbuff_in_bstrncpy(_out, fr_sbuff_current(_in), _len); \ if (_copied < 0) { \ fr_sbuff_advance(_in, fr_sbuff_in_bstrncpy(_out, fr_sbuff_current(_in), _len + _copied)); \ goto done;\ @@ -455,14 +454,14 @@ done: * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. * @param[in] until Characters which stop the copy operation. - * @param[in] escape If not '\0', ignore characters in the until set when + * @param[in] escape_chr If not '\0', ignore characters in the until set when * prefixed with this escape character. * @return * - 0 no bytes copied. * - >0 the number of bytes copied. */ size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1], char escape) + bool const until[static UINT8_MAX + 1], char escape_chr) { fr_sbuff_t our_in = FR_SBUFF_COPY(in); bool do_escape = false; /* Track state across extensions */ @@ -478,13 +477,13 @@ size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, p = our_in.p; end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in)); - if (escape == '\0') { + if (escape_chr == '\0') { while((p < end) && !until[(uint8_t)*p]) p++; } else { while (p < end) { if (do_escape) { do_escape = false; - } else if (*p == escape) { + } else if (*p == escape_chr) { do_escape = true; } else if (until[(uint8_t)*p]) { break; @@ -515,23 +514,20 @@ done: * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. * @param[in] until Characters which stop the copy operation. - * @param[in] escape Ignore characters in the until set when prefixed - * with this escape character. Must be specified. - * @param[in] escape_subs Escape characters and their substitutions. + * @param[in] rules for processing escape sequences. * @return * - 0 no bytes copied. * - >0 the number of bytes copied including escape sequences. */ size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, bool const until[static UINT8_MAX + 1], - char escape, - char const escape_subs[static UINT8_MAX + 1]) + fr_sbuff_escape_rules_t const *rules) { fr_sbuff_t our_in = FR_SBUFF_COPY(in); bool do_escape = false; /* Track state across extensions */ CHECK_SBUFF_INIT(in); - if (unlikely(escape == '\0')) return 0; + if (unlikely(rules->chr == '\0')) return 0; do { char *p; @@ -547,9 +543,58 @@ size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, do_escape = false; /* - * Not an actual escape seq + * Check for \x */ - if (escape_subs[(uint8_t)*p] == '\0') goto next; + if (rules->do_hex && (*p == 'x')) { + uint8_t escape; + fr_sbuff_marker_t m; + + fr_sbuff_marker(&m, &our_in); /* allow for backtrack */ + fr_sbuff_set(&our_in, ++p); /* sync sbuff */ + + if (fr_sbuff_out_uint8_hex(NULL, &escape, &our_in, false) != 2) { + fr_sbuff_set_to_marker(&m); /* backtrack */ + p = m.p; + goto check_subs; + } + + if (fr_sbuff_in_char(out, escape) <= 0) { + fr_sbuff_set_to_marker(&m); /* backtrack */ + goto done; + } + p = our_in.p; + continue; + } + + /* + * Check for \ + */ + if (rules->do_oct && isdigit(*p)) { + uint8_t escape; + fr_sbuff_marker_t m; + + fr_sbuff_marker(&m, &our_in); /* allow for backtrack */ + fr_sbuff_set(&our_in, p); /* sync sbuff */ + + if (fr_sbuff_out_uint8_oct(NULL, &escape, &our_in, false) != 3) { + fr_sbuff_set_to_marker(&m); /* backtrack */ + p = m.p; + goto check_subs; + } + + if (fr_sbuff_in_char(out, escape) <= 0) { + fr_sbuff_set_to_marker(&m); /* backtrack */ + goto done; + } + p = our_in.p; + continue; + } + + check_subs: + /* + * Not a recognised escape sequence. + */ + if (rules->subs[(uint8_t)*p] == '\0') goto next; /* * We already copied everything up @@ -557,18 +602,17 @@ size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, * write the substituted char to * the output buffer. */ - if (fr_sbuff_in_char(out, escape_subs[(uint8_t)*p]) <= 0) goto done; + if (fr_sbuff_in_char(out, rules->subs[(uint8_t)*p++]) <= 0) goto done; /* * ...and advance past the entire * escape seq in the input buffer. */ fr_sbuff_advance(&our_in, 2); - p++; continue; } - if (*p == escape) { + if (*p == rules->chr) { /* * Copy out any data we got before * we hit the escape char. @@ -1105,11 +1149,11 @@ size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allow * @param[in] sbuff sbuff to search in. * @param[in] len Maximum amount to advance by. Unconstrained if SIZE_MAX. * @param[in] until character set. - * @param[in] escape If not '\0', ignore characters in the until set when + * @param[in] escape_chr If not '\0', ignore characters in the until set when * prefixed with this escape character. * @return how many bytes we advanced. */ -size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, bool const until[static UINT8_MAX + 1], char escape) +size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, bool const until[static UINT8_MAX + 1], char escape_chr) { size_t total = 0; char const *p; @@ -1125,13 +1169,13 @@ size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, bool const until[static end = CONSTRAINED_END(sbuff, len, total); p = sbuff->p; - if (escape == '\0') { + if (escape_chr == '\0') { while ((p < end) && !until[(uint8_t)*p]) p++; } else { while (p < end) { if (do_escape) { do_escape = false; - } else if (*p == escape) { + } else if (*p == escape_chr) { do_escape = true; } else if (until[(uint8_t)*p]) { break; diff --git a/src/lib/util/sbuff.h b/src/lib/util/sbuff.h index 35469eaf6b2..125779b48a3 100644 --- a/src/lib/util/sbuff.h +++ b/src/lib/util/sbuff.h @@ -672,12 +672,21 @@ size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len bool const allowed[static UINT8_MAX + 1]); size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1], char escape); + bool const until[static UINT8_MAX + 1], char escape_chr); + +/** Set of parsing rules for *unescape_until functions + * + */ +typedef struct { + char chr; //!< Character at the start of an escape sequence. + char const subs[UINT8_MAX + 1]; //!< Special characters and their substitutions. + bool do_hex; //!< Process hex sequences i.e. \x. + bool do_oct; //!< Process oct sequences i.e. \. +} fr_sbuff_escape_rules_t; size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1], - char escape, - char const escape_subs[static UINT8_MAX + 1]); + bool const until[static UINT8_MAX + 1], + fr_sbuff_escape_rules_t const *rules); /** Find the longest prefix in an sbuff * @@ -733,8 +742,13 @@ static inline size_t fr_sbuff_out_abstrncpy_allowed(TALLOC_CTX *ctx, char **out, SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_bstrncpy_allowed, in, len, allowed); static inline size_t fr_sbuff_out_abstrncpy_until(TALLOC_CTX *ctx, char **out, fr_sbuff_t *in, size_t len, - bool const until[static UINT8_MAX + 1], char escape) -SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_bstrncpy_until, in, len, until, escape); + bool const until[static UINT8_MAX + 1], char escape_chr) +SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_bstrncpy_until, in, len, until, escape_chr); + +static inline size_t fr_sbuff_out_aunescape_until(TALLOC_CTX *ctx, char **out, fr_sbuff_t *in, size_t len, + bool const until[static UINT8_MAX + 1], + fr_sbuff_escape_rules_t const *rules) +SBUFF_OUT_TALLOC_FUNC_DEF(fr_sbuff_out_unescape_until, in, len, until, rules); /** @} */ /** @name Look for a token in a particular format, parse it, and write it to the output pointer @@ -752,19 +766,11 @@ size_t fr_sbuff_out_uint16(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_ size_t fr_sbuff_out_uint32(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint64(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int8_oct(fr_sbuff_parse_error_t *err, int8_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int16_oct(fr_sbuff_parse_error_t *err, int16_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int32_oct(fr_sbuff_parse_error_t *err, int32_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int64_oct(fr_sbuff_parse_error_t *err, int64_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint8_oct(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint16_oct(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint32_oct(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint64_oct(fr_sbuff_parse_error_t *err, uint64_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int8_hex(fr_sbuff_parse_error_t *err, int8_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int16_hex(fr_sbuff_parse_error_t *err, int16_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int32_hex(fr_sbuff_parse_error_t *err, int32_t *out, fr_sbuff_t *sbuff, bool no_trailing); -size_t fr_sbuff_out_int64_hex(fr_sbuff_parse_error_t *err, int64_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint8_hex(fr_sbuff_parse_error_t *err, uint8_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint16_hex(fr_sbuff_parse_error_t *err, uint16_t *out, fr_sbuff_t *sbuff, bool no_trailing); size_t fr_sbuff_out_uint32_hex(fr_sbuff_parse_error_t *err, uint32_t *out, fr_sbuff_t *sbuff, bool no_trailing); @@ -815,7 +821,7 @@ size_t fr_sbuff_adv_past_whitespace(fr_sbuff_t *sbuff, size_t len); size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool const allowed[static UINT8_MAX + 1]); -size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, bool const until[static UINT8_MAX + 1], char escape); +size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, bool const until[static UINT8_MAX + 1], char escape_chr); char *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *in, size_t len, char const *chr); @@ -890,6 +896,12 @@ static inline bool fr_sbuff_is_space(fr_sbuff_t *sbuff) if (FR_SBUFF_CANT_EXTEND(sbuff)) return false; return isspace(*sbuff->p); } + +static inline bool fr_sbuff_is_hex(fr_sbuff_t *sbuff) +{ + if (FR_SBUFF_CANT_EXTEND(sbuff)) return false; + return (isdigit(*sbuff->p) || ((tolower(*sbuff->p) >= 'a') && (tolower(*sbuff->p) <= 'f'))); +} /** @} */ #ifdef __cplusplus diff --git a/src/lib/util/sbuff_tests.c b/src/lib/util/sbuff_tests.c index 93805ff7f05..3067b42cd1c 100644 --- a/src/lib/util/sbuff_tests.c +++ b/src/lib/util/sbuff_tests.c @@ -387,13 +387,21 @@ static void test_bstrncpy_until(void) static void test_unescape_until(void) { - char const in[] = "i am a test string"; - char const in_long[] = "i am a longer test string"; - char const in_escapes[] = "i am a |t|est strin|g"; - char out[18 + 1]; - char escape_out[20 + 1]; - fr_sbuff_t sbuff; - ssize_t slen; + char const in[] = "i am a test string"; + char const in_long[] = "i am a longer test string"; + char const in_escapes[] = "i am a |t|est strin|g"; + char const in_escapes_seq[] = "i |x|0am a |t|est strin|g|x20|040"; + char out[18 + 1]; + char escape_out[20 + 1]; + + fr_sbuff_t sbuff; + ssize_t slen; + fr_sbuff_escape_rules_t rules = { .chr = '\\' }; + fr_sbuff_escape_rules_t pipe_rules = { .chr = '|', .subs = { ['g'] = 'g' } }; + fr_sbuff_escape_rules_t pipe_rules_sub = { .chr = '|', .subs = { ['g'] = 'h' } }; + fr_sbuff_escape_rules_t pipe_rules_sub_hex = { .chr = '|', .subs = { ['g'] = 'h' }, .do_hex = true }; + fr_sbuff_escape_rules_t pipe_rules_sub_oct = { .chr = '|', .subs = { ['g'] = 'h' }, .do_oct = true }; + fr_sbuff_escape_rules_t pipe_rules_both = { .chr = '|', .subs = { ['g'] = 'h' }, .do_hex = true, .do_oct = true }; fr_sbuff_init(&sbuff, in, sizeof(in)); @@ -404,14 +412,14 @@ static void test_unescape_until(void) */ TEST_CASE("Copy 5 bytes to out"); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 5, - (bool[UINT8_MAX + 1]){ }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ }, &rules); TEST_CHECK_SLEN(5, slen); TEST_CHECK_STRCMP("i am ", out); TEST_CHECK_STRCMP("a test string", sbuff.p); TEST_CASE("Copy 13 bytes to out"); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 13, - (bool[UINT8_MAX + 1]){ }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ }, &rules); TEST_CHECK_SLEN(13, slen); TEST_CHECK_STRCMP("a test string", out); TEST_CHECK_STRCMP("", sbuff.p); @@ -419,7 +427,7 @@ static void test_unescape_until(void) TEST_CASE("Copy would overrun input"); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 1, - (bool[UINT8_MAX + 1]){ }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ }, &rules); TEST_CHECK_SLEN(0, slen); TEST_CHECK(sbuff.p == sbuff.end); @@ -427,7 +435,7 @@ static void test_unescape_until(void) fr_sbuff_init(&sbuff, in_long, sizeof(in_long)); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, - SIZE_MAX, (bool[UINT8_MAX + 1]){ }, '\\', (char[UINT8_MAX + 1]){}); + SIZE_MAX, (bool[UINT8_MAX + 1]){ }, &rules); TEST_CHECK_SLEN(18, slen); TEST_CHECK_STRCMP("i am a longer test", out); @@ -435,7 +443,7 @@ static void test_unescape_until(void) fr_sbuff_set_to_start(&sbuff); out[0] = 'a'; slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, (size_t)1), &sbuff, - SIZE_MAX, (bool[UINT8_MAX + 1]){ }, '\\', (char[UINT8_MAX + 1]){}); + SIZE_MAX, (bool[UINT8_MAX + 1]){ }, &rules); TEST_CHECK_SLEN(0, slen); TEST_CHECK(out[0] == '\0'); /* should be set to \0 */ TEST_CHECK(sbuff.p == sbuff.start); @@ -444,7 +452,7 @@ static void test_unescape_until(void) fr_sbuff_set_to_start(&sbuff); out[0] = 'a'; slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, 0, - (bool[UINT8_MAX + 1]){ }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ }, &rules); TEST_CHECK_SLEN(0, slen); TEST_CHECK(out[0] == '\0'); /* should be set to \0 */ TEST_CHECK(sbuff.p == sbuff.start); @@ -455,28 +463,28 @@ static void test_unescape_until(void) TEST_CASE("Copy until first t"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, sizeof(out)), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['t'] = true }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ ['t'] = true }, &rules); TEST_CHECK_SLEN(14, slen); TEST_CHECK_STRCMP("i am a longer ", out); TEST_CASE("Copy until first t with length constraint (same len as token)"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, 15), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['t'] = true }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ ['t'] = true }, &rules); TEST_CHECK_SLEN(14, slen); TEST_CHECK_STRCMP("i am a longer ", out); TEST_CASE("Copy until first t with length constraint (one shorter than token)"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, 14), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['t'] = true }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ ['t'] = true }, &rules); TEST_CHECK_SLEN(13, slen); TEST_CHECK_STRCMP("i am a longer", out); TEST_CASE("Zero length token (should still be terminated)"); fr_sbuff_set_to_start(&sbuff); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(out, 14), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['i'] = true }, '\\', (char[UINT8_MAX + 1]){}); + (bool[UINT8_MAX + 1]){ ['i'] = true }, &rules); TEST_CHECK_SLEN(0, slen); TEST_CHECK_STRCMP("", out); @@ -486,9 +494,7 @@ static void test_unescape_until(void) TEST_CASE("Escape with substition to same char"); fr_sbuff_init(&sbuff, in_escapes, sizeof(in_escapes)); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['g'] = true }, - '|', - (char[UINT8_MAX + 1]){ ['g'] = 'g' }); + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules); TEST_CHECK_SLEN(21, slen); TEST_CHECK_STRCMP("i am a |t|est string", escape_out); TEST_CHECK_STRCMP("", sbuff.p); @@ -496,12 +502,75 @@ static void test_unescape_until(void) TEST_CASE("Escape with substition to different char"); fr_sbuff_init(&sbuff, in_escapes, sizeof(in_escapes)); slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(escape_out, sizeof(escape_out)), &sbuff, SIZE_MAX, - (bool[UINT8_MAX + 1]){ ['g'] = true }, - '|', - (char[UINT8_MAX + 1]){ ['g'] = 'h' }); + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules_sub); TEST_CHECK_SLEN(21, slen); TEST_CHECK_STRCMP("i am a |t|est strinh", escape_out); TEST_CHECK_STRCMP("", sbuff.p); + + { + char tmp_out[24 + 1]; + + TEST_CASE("Escape with hex substitutions (insufficient output space)"); + fr_sbuff_init(&sbuff, in_escapes_seq, sizeof(in_escapes_seq)); + slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(tmp_out, sizeof(tmp_out)), + &sbuff, SIZE_MAX, + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules_sub_hex); + TEST_CHECK_SLEN(25, slen); + TEST_CHECK_STRCMP("i |x|0am a |t|est strinh", tmp_out); + TEST_CHECK_STRCMP("|x20|040", sbuff.p); + } + + { + char tmp_out[25 + 1]; + + TEST_CASE("Escape with hex substitutions (sufficient output space)"); + fr_sbuff_init(&sbuff, in_escapes_seq, sizeof(in_escapes_seq)); + slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(tmp_out, sizeof(tmp_out)), + &sbuff, SIZE_MAX, + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules_sub_hex); + TEST_CHECK_SLEN(29, slen); + TEST_CHECK_STRCMP("i |x|0am a |t|est strinh ", tmp_out); + TEST_CHECK_STRCMP("|040", sbuff.p); + } + + { + char tmp_out[28 + 1]; + + TEST_CASE("Escape with oct substitutions (insufficient output space)"); + fr_sbuff_init(&sbuff, in_escapes_seq, sizeof(in_escapes_seq)); + slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(tmp_out, sizeof(tmp_out)), + &sbuff, SIZE_MAX, + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules_sub_oct); + TEST_CHECK_SLEN(29, slen); + TEST_CHECK_STRCMP("i |x|0am a |t|est strinh|x20", tmp_out); + TEST_CHECK_STRCMP("|040", sbuff.p); + } + + { + char tmp_out[29 + 1]; + + TEST_CASE("Escape with oct substitutions (sufficient output space)"); + fr_sbuff_init(&sbuff, in_escapes_seq, sizeof(in_escapes_seq)); + slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(tmp_out, sizeof(tmp_out)), + &sbuff, SIZE_MAX, + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules_sub_oct); + TEST_CHECK_SLEN(33, slen); + TEST_CHECK_STRCMP("i |x|0am a |t|est strinh|x20 ", tmp_out); + TEST_CHECK_STRCMP("", sbuff.p); + } + + { + char tmp_out[26 + 1]; + + TEST_CASE("Escape with hex and oct substitutions (sufficient output space)"); + fr_sbuff_init(&sbuff, in_escapes_seq, sizeof(in_escapes_seq)); + slen = fr_sbuff_out_unescape_until(&FR_SBUFF_TMP(tmp_out, sizeof(tmp_out)), + &sbuff, SIZE_MAX, + (bool[UINT8_MAX + 1]){ ['g'] = true }, &pipe_rules_both); + TEST_CHECK_SLEN(33, slen); + TEST_CHECK_STRCMP("i |x|0am a |t|est strinh ", tmp_out); + TEST_CHECK_STRCMP("", sbuff.p); + } } static void test_no_advance(void) @@ -1160,7 +1229,7 @@ TEST_LIST = { { "fr_sbuff_out_bstrncpy", test_bstrncpy }, { "fr_sbuff_out_bstrncpy_allowed", test_bstrncpy_allowed }, { "fr_sbuff_out_bstrncpy_until", test_bstrncpy_until }, - { "fr_sbuff_out_descape_until", test_unescape_until }, + { "fr_sbuff_out_unescape_until", test_unescape_until }, /* * Extending buffer