mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
lws_strexp: flexible string expansion helper
This commit is contained in:
parent
10290048b0
commit
fb1b2842fd
3 changed files with 291 additions and 0 deletions
|
@ -141,3 +141,99 @@ lws_tokenize(struct lws_tokenize *ts);
|
|||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_tokenize_cstr(struct lws_tokenize *ts, char *str, int max);
|
||||
|
||||
|
||||
/*
|
||||
* lws_strexp: flexible string expansion helper api
|
||||
*
|
||||
* This stateful helper can handle multiple separate input chunks and multiple
|
||||
* output buffer loads with arbitrary boundaries between literals and expanded
|
||||
* symbols. This allows it to handle fragmented input as well as arbitrarily
|
||||
* long symbol expansions that are bigger than the output buffer itself.
|
||||
*
|
||||
* A user callback is used to convert symbol names to the symbol value.
|
||||
*
|
||||
* A single byte buffer for input and another for output can process any
|
||||
* length substitution then. The state object is around 64 bytes on a 64-bit
|
||||
* system and it only uses 8 bytes stack.
|
||||
*/
|
||||
|
||||
|
||||
typedef int (*lws_strexp_expand_cb)(void *priv, const char *name, char *out,
|
||||
size_t *pos, size_t olen, size_t *exp_ofs);
|
||||
|
||||
typedef struct lws_strexp {
|
||||
char name[32];
|
||||
lws_strexp_expand_cb cb;
|
||||
void *priv;
|
||||
char *out;
|
||||
size_t olen;
|
||||
size_t pos;
|
||||
|
||||
size_t exp_ofs;
|
||||
|
||||
uint8_t name_pos;
|
||||
char state;
|
||||
} lws_strexp_t;
|
||||
|
||||
enum {
|
||||
LSTRX_DONE, /* it completed OK */
|
||||
LSTRX_FILLED_OUT, /* out buf filled and needs resetting */
|
||||
LSTRX_FATAL_NAME_TOO_LONG = -1, /* fatal */
|
||||
LSTRX_FATAL_NAME_UNKNOWN = -2,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* lws_strexp_init() - initialize an lws_strexp_t for use
|
||||
*
|
||||
* \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 olen: the length of the output buffer in bytes
|
||||
*
|
||||
* Prepares an lws_strexp_t for use and sets the initial output buffer
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb,
|
||||
char *out, size_t olen);
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen);
|
||||
|
||||
/**
|
||||
* lws_strexp_expand() - copy / expand a string into the output buffer
|
||||
*
|
||||
* \p exp: the exp object for the copy / expansion
|
||||
* \p in: the start of the next input data
|
||||
* \p len: the length of the input data
|
||||
* \p pused: pointer to write the amount of input used
|
||||
*
|
||||
* Copies in to the output buffer set in exp, expanding any ${name} tokens using
|
||||
* the callback. *pused is set to the number of input chars used.
|
||||
*
|
||||
* May return LSTRX_FILLED_OUT early with *pused < len if the output buffer is
|
||||
* filled. Handle the output buffer and reset it with lws_strexp_reset_out()
|
||||
* before calling again with adjusted in / len to continue.
|
||||
*
|
||||
* In the case of large expansions, the expansion itself may fill the output
|
||||
* buffer, in which case the expansion callback returns the LSTRX_FILLED_OUT
|
||||
* and will be called again to continue with its *exp_ofs parameter set
|
||||
* appropriately.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len,
|
||||
size_t *pused_in, size_t *pused_out);
|
||||
|
||||
|
|
|
@ -911,6 +911,112 @@ lws_tokenize_init(struct lws_tokenize *ts, const char *start, int flags)
|
|||
ts->delim = LWSTZ_DT_NEED_FIRST_CONTENT;
|
||||
}
|
||||
|
||||
|
||||
typedef enum {
|
||||
LWS_EXPS_LITERAL,
|
||||
LWS_EXPS_OPEN_OR_LIT,
|
||||
LWS_EXPS_NAME_OR_CLOSE,
|
||||
LWS_EXPS_DRAIN,
|
||||
} lws_strexp_state;
|
||||
|
||||
void
|
||||
lws_strexp_init(lws_strexp_t *exp, void *priv, lws_strexp_expand_cb cb,
|
||||
char *out, size_t olen)
|
||||
{
|
||||
memset(exp, 0, sizeof(*exp));
|
||||
exp->cb = cb;
|
||||
exp->out = out;
|
||||
exp->olen = olen;
|
||||
exp->state = LWS_EXPS_LITERAL;
|
||||
exp->priv = priv;
|
||||
}
|
||||
|
||||
void
|
||||
lws_strexp_reset_out(lws_strexp_t *exp, char *out, size_t olen)
|
||||
{
|
||||
exp->out = out;
|
||||
exp->olen = olen;
|
||||
exp->pos = 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_strexp_expand(lws_strexp_t *exp, const char *in, size_t len,
|
||||
size_t *pused_in, size_t *pused_out)
|
||||
{
|
||||
size_t used = 0;
|
||||
int n;
|
||||
|
||||
while (used < len) {
|
||||
|
||||
switch (exp->state) {
|
||||
case LWS_EXPS_LITERAL:
|
||||
if (*in == '$') {
|
||||
exp->state = LWS_EXPS_OPEN_OR_LIT;
|
||||
break;
|
||||
}
|
||||
|
||||
exp->out[exp->pos++] = *in;
|
||||
if (exp->olen - exp->pos < 1) {
|
||||
*pused_in = used + 1;
|
||||
*pused_out = exp->pos;
|
||||
return LSTRX_FILLED_OUT;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_EXPS_OPEN_OR_LIT:
|
||||
if (*in == '{') {
|
||||
exp->state = LWS_EXPS_NAME_OR_CLOSE;
|
||||
exp->name_pos = 0;
|
||||
break;
|
||||
}
|
||||
/* treat as a literal */
|
||||
if (exp->olen - exp->pos < 3)
|
||||
return -1;
|
||||
|
||||
exp->out[exp->pos++] = '$';
|
||||
exp->out[exp->pos++] = *in;
|
||||
if (*in != '$')
|
||||
exp->state = LWS_EXPS_LITERAL;
|
||||
break;
|
||||
|
||||
case LWS_EXPS_NAME_OR_CLOSE:
|
||||
if (*in == '}') {
|
||||
exp->name[exp->name_pos] = '\0';
|
||||
exp->state = LWS_EXPS_DRAIN;
|
||||
goto drain;
|
||||
}
|
||||
if (exp->name_pos >= sizeof(exp->name) - 1)
|
||||
return LSTRX_FATAL_NAME_TOO_LONG;
|
||||
|
||||
exp->name[exp->name_pos++] = *in;
|
||||
break;
|
||||
|
||||
case LWS_EXPS_DRAIN:
|
||||
drain:
|
||||
*pused_in = used;
|
||||
n = exp->cb(exp->priv, exp->name, exp->out, &exp->pos,
|
||||
exp->olen, &exp->exp_ofs);
|
||||
*pused_out = exp->pos;
|
||||
if (n == LSTRX_FILLED_OUT ||
|
||||
n == LSTRX_FATAL_NAME_UNKNOWN)
|
||||
return n;
|
||||
|
||||
exp->state = LWS_EXPS_LITERAL;
|
||||
break;
|
||||
}
|
||||
|
||||
used++;
|
||||
in++;
|
||||
}
|
||||
|
||||
exp->out[exp->pos] = '\0';
|
||||
*pused_in = used;
|
||||
*pused_out = exp->pos;
|
||||
|
||||
return LSTRX_DONE;
|
||||
}
|
||||
|
||||
|
||||
#if LWS_MAX_SMP > 1
|
||||
|
||||
void
|
||||
|
|
|
@ -277,6 +277,40 @@ static const char *element_names[] = {
|
|||
"LWS_TOKZE_QUOTED_STRING",
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
exp_cb1(void *priv, const char *name, char *out, size_t *pos, size_t olen,
|
||||
size_t *exp_ofs)
|
||||
{
|
||||
const char *replace = NULL;
|
||||
size_t total, budget;
|
||||
|
||||
if (!strcmp(name, "test")) {
|
||||
replace = "replacement_string";
|
||||
total = strlen(replace);
|
||||
goto expand;
|
||||
}
|
||||
|
||||
return LSTRX_FATAL_NAME_UNKNOWN;
|
||||
|
||||
expand:
|
||||
budget = olen - *pos;
|
||||
total -= *exp_ofs;
|
||||
if (total < budget)
|
||||
budget = total;
|
||||
|
||||
memcpy(out + *pos, replace + (*exp_ofs), budget);
|
||||
*exp_ofs += budget;
|
||||
*pos += budget;
|
||||
|
||||
if (budget == total)
|
||||
return LSTRX_DONE;
|
||||
|
||||
return LSTRX_FILLED_OUT;
|
||||
}
|
||||
|
||||
static const char *exp_inp1 = "this-is-a-${test}-for-strexp";
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_tokenize ts;
|
||||
|
@ -301,6 +335,61 @@ int main(int argc, const char **argv)
|
|||
if ((p = lws_cmdline_option(argc, argv, "-f")))
|
||||
flags = atoi(p);
|
||||
|
||||
/* lws_strexp */
|
||||
|
||||
{
|
||||
size_t in_len, used_in, used_out;
|
||||
lws_strexp_t exp;
|
||||
char obuf[128];
|
||||
const char *p;
|
||||
|
||||
obuf[0] = '\0';
|
||||
lws_strexp_init(&exp, NULL, exp_cb1, obuf, sizeof(obuf));
|
||||
n = lws_strexp_expand(&exp, exp_inp1, 28, &used_in, &used_out);
|
||||
if (n != LSTRX_DONE || used_in != 28 ||
|
||||
strcmp(obuf, "this-is-a-replacement_string-for-strexp")) {
|
||||
lwsl_notice("%s: obuf %s\n", __func__, obuf);
|
||||
lwsl_err("%s: lws_strexp test 1 failed: %d\n", __func__, n);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = exp_inp1;
|
||||
in_len = strlen(p);
|
||||
memset(obuf, 0, sizeof(obuf));
|
||||
lws_strexp_init(&exp, NULL, exp_cb1, obuf, 16);
|
||||
n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out);
|
||||
if (n != LSTRX_FILLED_OUT || used_in != 16 || used_out != 16) {
|
||||
lwsl_err("a\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
p += used_in;
|
||||
in_len -= used_in;
|
||||
|
||||
memset(obuf, 0, sizeof(obuf));
|
||||
lws_strexp_reset_out(&exp, obuf, 16);
|
||||
|
||||
n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out);
|
||||
if (n != LSTRX_FILLED_OUT || used_in != 5 || used_out != 16) {
|
||||
lwsl_err("b: n %d, used_in %d, used_out %d\n", n,
|
||||
(int)used_in, (int)used_out);
|
||||
return 2;
|
||||
}
|
||||
|
||||
p += used_in;
|
||||
in_len -= used_in;
|
||||
|
||||
memset(obuf, 0, sizeof(obuf));
|
||||
lws_strexp_reset_out(&exp, obuf, 16);
|
||||
|
||||
n = lws_strexp_expand(&exp, p, in_len, &used_in, &used_out);
|
||||
if (n != LSTRX_DONE || used_in != 7 || used_out != 7) {
|
||||
lwsl_err("c: n %d, used_in %d, used_out %d\n", n, (int)used_in, (int)used_out);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check lws_strnncpy() */
|
||||
|
||||
lws_strnncpy(dotstar, "12345678", 4, sizeof(dotstar));
|
||||
|
|
Loading…
Add table
Reference in a new issue