*/
#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;\
* @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 */
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;
* @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;
do_escape = false;
/*
- * Not an actual escape seq
+ * Check for \x<hex><hex>
*/
- 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 \<oct><oct><oct>
+ */
+ 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
* 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.
* @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;
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;
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<hex><hex>.
+ bool do_oct; //!< Process oct sequences i.e. \<oct><oct><oct>.
+} 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
*
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
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);
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);
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
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));
*/
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);
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);
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);
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);
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);
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);
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);
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)
{ "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