From ac6edaf19970554ac5380f7d41057a05aed1a187 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 15 Jun 2020 15:01:35 +0100 Subject: [PATCH] lws_strexp: add ability to find output length without write Sometimes we need to find out the substituted length before we can allocate and actually store it. Teach strexp that if we set the output buffer to NULL (and the output length to something big) we are asking for the substituted length and to not produce output. --- include/libwebsockets/lws-tokenize.h | 14 ++++++++++++-- lib/core/libwebsockets.c | 14 ++++++++++---- lib/secure-streams/secure-streams.c | 3 ++- .../api-tests/api-test-lws_tokenize/main.c | 14 +++++++++++++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/include/libwebsockets/lws-tokenize.h b/include/libwebsockets/lws-tokenize.h index e9e6851fc..70379de0b 100644 --- a/include/libwebsockets/lws-tokenize.h +++ b/include/libwebsockets/lws-tokenize.h @@ -192,10 +192,15 @@ enum { * \p exp: the exp object to init * \p priv: the user's object pointer to pass to callback * \p cb: the callback to expand named objects - * \p out: the start of the output buffer + * \p out: the start of the output buffer, or NULL just to get the length * \p olen: the length of the output buffer in bytes * * Prepares an lws_strexp_t for use and sets the initial output buffer + * + * If \p out is NULL, substitution proceeds normally, but no output is produced, + * only the length is returned. olen should be set to the largest feasible + * overall length. To use this mode, the substitution callback must also check + * for NULL \p out and avoid producing the output. */ LWS_VISIBLE LWS_EXTERN void lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb, @@ -205,12 +210,17 @@ lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb, * lws_strexp_reset_out() - reset the output buffer on an existing strexp * * \p exp: the exp object to init - * \p out: the start of the output buffer + * \p out: the start of the output buffer, or NULL to just get length * \p olen: the length of the output buffer in bytes * * Provides a new output buffer for lws_strexp_expand() to continue to write * into. It can be the same as the old one if it has been copied out or used. * The position of the next write will be reset to the start of the given buf. + * + * If \p out is NULL, substitution proceeds normally, but no output is produced, + * only the length is returned. \p olen should be set to the largest feasible + * overall length. To use this mode, the substitution callback must also check + * for NULL \p out and avoid producing the output. */ LWS_VISIBLE LWS_EXTERN void lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen); diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index 4688b3e23..0ad9330d6 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -936,7 +936,9 @@ lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len, break; } - exp->out[exp->pos++] = *in; + if (exp->out) + exp->out[exp->pos] = *in; + exp->pos++; if (exp->olen - exp->pos < 1) { *pused_in = used + 1; *pused_out = exp->pos; @@ -955,8 +957,11 @@ lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len, if (exp->olen - exp->pos < 3) return -1; - exp->out[exp->pos++] = '$'; - exp->out[exp->pos++] = *in; + if (exp->out) { + exp->out[exp->pos++] = '$'; + exp->out[exp->pos++] = *in; + } else + exp->pos += 2; if (*in != '$') exp->state = LWS_EXPS_LITERAL; break; @@ -991,7 +996,8 @@ drain: in++; } - exp->out[exp->pos] = '\0'; + if (exp->out) + exp->out[exp->pos] = '\0'; *pused_in = used; *pused_out = exp->pos; diff --git a/lib/secure-streams/secure-streams.c b/lib/secure-streams/secure-streams.c index 7318b4f17..a8f4ed84f 100644 --- a/lib/secure-streams/secure-streams.c +++ b/lib/secure-streams/secure-streams.c @@ -167,7 +167,8 @@ lws_ss_exp_cb_metadata(void *priv, const char *name, char *out, size_t *pos, if (total < budget) budget = total; - memcpy(out + *pos, replace + (*exp_ofs), budget); + if (out) + memcpy(out + *pos, replace + (*exp_ofs), budget); *exp_ofs += budget; *pos += budget; diff --git a/minimal-examples/api-tests/api-test-lws_tokenize/main.c b/minimal-examples/api-tests/api-test-lws_tokenize/main.c index 9cbc2e3ce..7682ede1f 100644 --- a/minimal-examples/api-tests/api-test-lws_tokenize/main.c +++ b/minimal-examples/api-tests/api-test-lws_tokenize/main.c @@ -299,7 +299,8 @@ expand: if (total < budget) budget = total; - memcpy(out + *pos, replace + (*exp_ofs), budget); + if (out) + memcpy(out + *pos, replace + (*exp_ofs), budget); *exp_ofs += budget; *pos += budget; @@ -354,6 +355,17 @@ int main(int argc, const char **argv) return 1; } + /* as above, but don't generate output, just find the length */ + + lws_strexp_init(&exp, NULL, exp_cb1, NULL, (size_t)-1); + n = lws_strexp_expand(&exp, exp_inp1, 28, &used_in, &used_out); + if (n != LSTRX_DONE || used_in != 28 || used_out != 39) { + lwsl_err("%s: lws_strexp test 2 failed: %d, used_out: %d\n", + __func__, n, (int)used_out); + + return 1; + } + p = exp_inp1; in_len = strlen(p); memset(obuf, 0, sizeof(obuf));