[mod_ssi] fix ancient bugs; replace cond parser
parent
0d9a8ed0aa
commit
43c5093a20
|
@ -15,11 +15,9 @@ typedef struct {
|
|||
size_t offset;
|
||||
size_t size;
|
||||
|
||||
int line_pos;
|
||||
|
||||
int in_key;
|
||||
int in_brace;
|
||||
int in_cond;
|
||||
int depth;
|
||||
handler_ctx *p;
|
||||
} ssi_tokenizer_t;
|
||||
|
||||
ssi_val_t *ssi_val_init(void) {
|
||||
|
@ -27,6 +25,7 @@ ssi_val_t *ssi_val_init(void) {
|
|||
|
||||
s = calloc(1, sizeof(*s));
|
||||
force_assert(s);
|
||||
s->str = buffer_init();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -38,196 +37,161 @@ void ssi_val_free(ssi_val_t *s) {
|
|||
}
|
||||
|
||||
__attribute_pure__
|
||||
int ssi_val_tobool(ssi_val_t *B) {
|
||||
if (B->type == SSI_TYPE_STRING) {
|
||||
return !buffer_is_blank(B->str);
|
||||
} else {
|
||||
return B->bo;
|
||||
}
|
||||
int ssi_val_tobool(const ssi_val_t *B) {
|
||||
return B->type == SSI_TYPE_BOOL ? B->bo : !buffer_is_blank(B->str);
|
||||
}
|
||||
|
||||
static int ssi_expr_tokenizer(handler_ctx *p,
|
||||
ssi_tokenizer_t *t, int *token_id, buffer *token) {
|
||||
int tid = 0;
|
||||
__attribute_pure__
|
||||
static int ssi_eval_expr_cmp(const ssi_val_t * const v1, const ssi_val_t * const v2, const int cond) {
|
||||
int cmp = (v1->type != SSI_TYPE_BOOL && v2->type != SSI_TYPE_BOOL)
|
||||
? strcmp(v1->str->ptr ? v1->str->ptr : "",
|
||||
v2->str->ptr ? v2->str->ptr : "")
|
||||
: ssi_val_tobool(v1) - ssi_val_tobool(v2);
|
||||
switch (cond) {
|
||||
case TK_EQ: return (cmp == 0);
|
||||
case TK_NE: return (cmp != 0);
|
||||
case TK_GE: return (cmp >= 0);
|
||||
case TK_GT: return (cmp > 0);
|
||||
case TK_LE: return (cmp <= 0);
|
||||
case TK_LT: return (cmp < 0);
|
||||
default: return 0;/*(should not happen)*/
|
||||
}
|
||||
}
|
||||
|
||||
__attribute_pure__
|
||||
static int ssi_eval_expr_cmp_bool(const ssi_val_t * const v1, const ssi_val_t * const v2, const int cond) {
|
||||
return (cond == TK_OR)
|
||||
? ssi_val_tobool(v1) || ssi_val_tobool(v2) /* TK_OR */
|
||||
: ssi_val_tobool(v1) && ssi_val_tobool(v2); /* TK_AND */
|
||||
}
|
||||
|
||||
static void ssi_eval_expr_append_val(buffer * const b, const char *s, const size_t slen) {
|
||||
if (buffer_is_blank(b))
|
||||
buffer_append_string_len(b, s, slen);
|
||||
else
|
||||
buffer_append_str2(b, CONST_STR_LEN(""), s, slen);
|
||||
}
|
||||
|
||||
static int ssi_expr_tokenizer(ssi_tokenizer_t * const t, buffer * const token) {
|
||||
size_t i;
|
||||
|
||||
for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) {
|
||||
char c = t->input[t->offset];
|
||||
const data_string *ds;
|
||||
while (t->offset < t->size
|
||||
&& (t->input[t->offset] == ' ' || t->input[t->offset] == '\t')) {
|
||||
++t->offset;
|
||||
}
|
||||
if (t->offset >= t->size)
|
||||
return 0;
|
||||
if (t->input[t->offset] == '\0') {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu foobar", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
switch (t->input[t->offset]) {
|
||||
case '=':
|
||||
tid = TK_EQ;
|
||||
|
||||
#if 0 /*(maybe accept "==", too)*/
|
||||
if (t->input[t->offset + 1] == '=')
|
||||
++t->offset;
|
||||
#endif
|
||||
t->offset++;
|
||||
t->line_pos++;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(=)"));
|
||||
|
||||
break;
|
||||
return TK_EQ;
|
||||
case '>':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
t->line_pos += 2;
|
||||
|
||||
tid = TK_GE;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(>=)"));
|
||||
return TK_GE;
|
||||
} else {
|
||||
t->offset += 1;
|
||||
t->line_pos += 1;
|
||||
|
||||
tid = TK_GT;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(>)"));
|
||||
return TK_GT;
|
||||
}
|
||||
|
||||
break;
|
||||
case '<':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
t->line_pos += 2;
|
||||
|
||||
tid = TK_LE;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(<=)"));
|
||||
return TK_LE;
|
||||
} else {
|
||||
t->offset += 1;
|
||||
t->line_pos += 1;
|
||||
|
||||
tid = TK_LT;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(<)"));
|
||||
return TK_LT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '!':
|
||||
if (t->input[t->offset + 1] == '=') {
|
||||
t->offset += 2;
|
||||
t->line_pos += 2;
|
||||
|
||||
tid = TK_NE;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(!=)"));
|
||||
return TK_NE;
|
||||
} else {
|
||||
t->offset += 1;
|
||||
t->line_pos += 1;
|
||||
|
||||
tid = TK_NOT;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(!)"));
|
||||
return TK_NOT;
|
||||
}
|
||||
|
||||
break;
|
||||
case '&':
|
||||
if (t->input[t->offset + 1] == '&') {
|
||||
t->offset += 2;
|
||||
t->line_pos += 2;
|
||||
|
||||
tid = TK_AND;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(&&)"));
|
||||
return TK_AND;
|
||||
} else {
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"pos: %d missing second &", t->line_pos);
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing second &", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
case '|':
|
||||
if (t->input[t->offset + 1] == '|') {
|
||||
t->offset += 2;
|
||||
t->line_pos += 2;
|
||||
|
||||
tid = TK_OR;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("(||)"));
|
||||
return TK_OR;
|
||||
} else {
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"pos: %d missing second |", t->line_pos);
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing second |", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
case '\t':
|
||||
case ' ':
|
||||
t->offset++;
|
||||
t->line_pos++;
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
/* search for the terminating " */
|
||||
for (i = 1; t->input[t->offset + i] && t->input[t->offset + i] != '\''; i++);
|
||||
|
||||
if (t->input[t->offset + i]) {
|
||||
tid = TK_VALUE;
|
||||
|
||||
buffer_copy_string_len(token, t->input + t->offset + 1, i-1);
|
||||
|
||||
t->offset += i + 1;
|
||||
t->line_pos += i + 1;
|
||||
} else {
|
||||
/* ERROR */
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"pos: %d missing closing quote", t->line_pos);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
case '(':
|
||||
t->offset++;
|
||||
t->in_brace++;
|
||||
|
||||
tid = TK_LPARAN;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN("("));
|
||||
break;
|
||||
return TK_LPARAN;
|
||||
case ')':
|
||||
t->offset++;
|
||||
t->in_brace--;
|
||||
return TK_RPARAN;
|
||||
case '\'':
|
||||
/* search for the terminating "'" */
|
||||
for (i = 1; t->input[t->offset + i] && t->input[t->offset + i] != '\''; i++);
|
||||
|
||||
tid = TK_RPARAN;
|
||||
|
||||
buffer_copy_string_len(token, CONST_STR_LEN(")"));
|
||||
break;
|
||||
case '$':
|
||||
if (t->input[t->offset + i]) {
|
||||
ssi_eval_expr_append_val(token, t->input + t->offset + 1, i-1);
|
||||
t->offset += i + 1;
|
||||
return TK_VALUE;
|
||||
} else {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing closing quote", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
case '$': {
|
||||
const char *var;
|
||||
size_t varlen;
|
||||
if (t->input[t->offset + 1] == '{') {
|
||||
for (i = 2; t->input[t->offset + i] && t->input[t->offset + i] != '}'; i++);
|
||||
|
||||
if (t->input[t->offset + i] != '}') {
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"pos: %d missing closing curly-brace", t->line_pos);
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu missing closing curly-brace", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_copy_string_len(token, t->input + t->offset + 2, i-3);
|
||||
++i; /* step past '}' */
|
||||
var = t->input + t->offset + 2;
|
||||
varlen = i-3;
|
||||
} else {
|
||||
for (i = 1; isalpha(((unsigned char *)t->input)[t->offset + i]) ||
|
||||
t->input[t->offset + i] == '_' ||
|
||||
((i > 1) && isdigit(((unsigned char *)t->input)[t->offset + i])); i++);
|
||||
|
||||
buffer_copy_string_len(token, t->input + t->offset + 1, i-1);
|
||||
}
|
||||
|
||||
tid = TK_VALUE;
|
||||
|
||||
if (NULL != (ds = (const data_string *)array_get_element_klen(p->ssi_cgi_env, BUF_PTR_LEN(token)))) {
|
||||
buffer_copy_buffer(token, &ds->value);
|
||||
} else if (NULL != (ds = (const data_string *)array_get_element_klen(p->ssi_vars, BUF_PTR_LEN(token)))) {
|
||||
buffer_copy_buffer(token, &ds->value);
|
||||
} else {
|
||||
buffer_copy_string_len(token, CONST_STR_LEN(""));
|
||||
var = t->input + t->offset + 1;
|
||||
varlen = i-1;
|
||||
}
|
||||
|
||||
const data_string *ds;
|
||||
if (NULL != (ds = (const data_string *)array_get_element_klen(t->p->ssi_cgi_env, var, varlen))
|
||||
|| NULL != (ds = (const data_string *)array_get_element_klen(t->p->ssi_vars, var, varlen)))
|
||||
ssi_eval_expr_append_val(token, BUF_PTR_LEN(&ds->value));
|
||||
t->offset += i;
|
||||
t->line_pos += i;
|
||||
|
||||
break;
|
||||
return TK_VALUE;
|
||||
}
|
||||
default:
|
||||
for (i = 0; isgraph(((unsigned char *)t->input)[t->offset + i]); i++) {
|
||||
char d = t->input[t->offset + i];
|
||||
switch(d) {
|
||||
default: continue;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case ')':
|
||||
|
@ -241,78 +205,129 @@ static int ssi_expr_tokenizer(handler_ctx *p,
|
|||
case '|':
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tid = TK_VALUE;
|
||||
|
||||
buffer_copy_string_len(token, t->input + t->offset, i);
|
||||
|
||||
ssi_eval_expr_append_val(token, t->input + t->offset, i);
|
||||
t->offset += i;
|
||||
t->line_pos += i;
|
||||
|
||||
break;
|
||||
return TK_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tid) {
|
||||
*token_id = tid;
|
||||
static int ssi_eval_expr_loop(ssi_tokenizer_t * const t, ssi_val_t * const v);
|
||||
|
||||
return 1;
|
||||
} else if (t->offset < t->size) {
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"pos: %d foobar", t->line_pos);
|
||||
}
|
||||
return 0;
|
||||
static int ssi_eval_expr_step(ssi_tokenizer_t * const t, ssi_val_t * const v) {
|
||||
buffer_clear(v->str);
|
||||
v->type = SSI_TYPE_UNSET; /*(not SSI_TYPE_BOOL)*/
|
||||
int next;
|
||||
const int level = t->in_brace;
|
||||
switch ((next = ssi_expr_tokenizer(t, v->str))) {
|
||||
case TK_VALUE:
|
||||
do { next=ssi_expr_tokenizer(t, v->str); } while (next == TK_VALUE);
|
||||
return next;
|
||||
case TK_LPARAN:
|
||||
if (t->in_brace > 16) return -1; /*(arbitrary limit)*/
|
||||
next = ssi_eval_expr_loop(t, v);
|
||||
if (next == TK_RPARAN && level == t->in_brace) {
|
||||
int result = ssi_val_tobool(v);
|
||||
next = ssi_eval_expr_step(t, v); /*(resets v)*/
|
||||
v->bo = result;
|
||||
v->type = SSI_TYPE_BOOL;
|
||||
return (next==TK_AND || next==TK_OR || next==TK_RPARAN || 0==next)
|
||||
? next
|
||||
: -1;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
case TK_RPARAN:
|
||||
return t->in_brace >= 0 ? TK_RPARAN : -1;
|
||||
case TK_NOT:
|
||||
if (++t->depth > 16) return -1; /*(arbitrary limit)*/
|
||||
next = ssi_eval_expr_step(t, v);
|
||||
--t->depth;
|
||||
if (-1 == next) return next;
|
||||
v->bo = !ssi_val_tobool(v);
|
||||
v->type = SSI_TYPE_BOOL;
|
||||
return next;
|
||||
default:
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop_cmp(ssi_tokenizer_t * const t, ssi_val_t * const v1, int cond) {
|
||||
ssi_val_t v2 = { SSI_TYPE_UNSET, NULL, 0 };
|
||||
v2.str = buffer_init();
|
||||
int next = ssi_eval_expr_step(t, &v2);
|
||||
if (-1 != next) {
|
||||
v1->bo = ssi_eval_expr_cmp(v1, &v2, cond);
|
||||
v1->type = SSI_TYPE_BOOL;
|
||||
}
|
||||
buffer_free(v2.str);
|
||||
return next;
|
||||
}
|
||||
|
||||
static int ssi_eval_expr_loop(ssi_tokenizer_t * const t, ssi_val_t * const v1) {
|
||||
int next = ssi_eval_expr_step(t, v1);
|
||||
switch (next) {
|
||||
case TK_AND: case TK_OR:
|
||||
break;
|
||||
case TK_EQ: case TK_NE:
|
||||
case TK_GT: case TK_GE:
|
||||
case TK_LT: case TK_LE:
|
||||
next = ssi_eval_expr_loop_cmp(t, v1, next);
|
||||
if (next == TK_RPARAN || 0 == next) return next;
|
||||
if (next != TK_AND && next != TK_OR) {
|
||||
log_error(t->p->errh, __FILE__, __LINE__,
|
||||
"pos: %zu parser failed somehow near here", t->offset+1);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return next;
|
||||
}
|
||||
|
||||
/*(Note: '&&' and '||' evaluations are not short-circuited)*/
|
||||
ssi_val_t v2 = { SSI_TYPE_UNSET, NULL, 0 };
|
||||
v2.str = buffer_init();
|
||||
do {
|
||||
int cond = next;
|
||||
next = ssi_eval_expr_step(t, &v2);
|
||||
switch (next) {
|
||||
case TK_AND: case TK_OR: case 0:
|
||||
break;
|
||||
case TK_EQ: case TK_NE:
|
||||
case TK_GT: case TK_GE:
|
||||
case TK_LT: case TK_LE:
|
||||
next = ssi_eval_expr_loop_cmp(t, &v2, next);
|
||||
if (-1 != next)
|
||||
break;
|
||||
__attribute_fallthrough__
|
||||
default:
|
||||
buffer_free(v2.str);
|
||||
return next;
|
||||
}
|
||||
v1->bo = ssi_eval_expr_cmp_bool(v1, &v2, cond);
|
||||
v1->type = SSI_TYPE_BOOL;
|
||||
} while (next == TK_AND || next == TK_OR);
|
||||
buffer_free(v2.str);
|
||||
return next;
|
||||
}
|
||||
|
||||
int ssi_eval_expr(handler_ctx *p, const char *expr) {
|
||||
ssi_tokenizer_t t;
|
||||
void *pParser;
|
||||
int token_id;
|
||||
buffer *token;
|
||||
ssi_ctx_t context;
|
||||
int ret;
|
||||
|
||||
t.input = expr;
|
||||
t.offset = 0;
|
||||
t.size = strlen(expr);
|
||||
t.line_pos = 1;
|
||||
|
||||
t.in_key = 1;
|
||||
t.in_brace = 0;
|
||||
t.in_cond = 0;
|
||||
t.depth = 0;
|
||||
t.p = p;
|
||||
|
||||
memset(&context.val, 0, sizeof(ssi_val_t));
|
||||
context.ok = 1;
|
||||
ssi_val_t v = { SSI_TYPE_UNSET, NULL, 0 };
|
||||
v.str = buffer_init();
|
||||
int rc = ssi_eval_expr_loop(&t, &v);
|
||||
rc = (0 == rc && 0 == t.in_brace && 0 == t.depth)
|
||||
? ssi_val_tobool(&v)
|
||||
: -1;
|
||||
buffer_free(v.str);
|
||||
|
||||
/* default context */
|
||||
|
||||
pParser = ssiexprparserAlloc( malloc );
|
||||
force_assert(pParser);
|
||||
token = buffer_init();
|
||||
while((1 == (ret = ssi_expr_tokenizer(p, &t, &token_id, token))) && context.ok) {
|
||||
ssiexprparser(pParser, token_id, token, &context);
|
||||
|
||||
token = buffer_init();
|
||||
}
|
||||
ssiexprparser(pParser, 0, token, &context);
|
||||
ssiexprparserFree(pParser, free );
|
||||
|
||||
buffer_free(token);
|
||||
|
||||
if (ret == -1) {
|
||||
log_error(p->errh, __FILE__, __LINE__, "expr parser failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (context.ok == 0) {
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"pos: %d parser failed somehow near here", t.line_pos);
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
log_error(p->errh, __FILE__, __LINE__,
|
||||
"expr: %s %d", expr, context.val.bo);
|
||||
#endif
|
||||
return context.val.bo;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ void *ssiexprparserAlloc(void *(*mallocProc)(size_t));
|
|||
void ssiexprparserFree(void *p, void (*freeProc)(void*));
|
||||
void ssiexprparser(void *yyp, int yymajor, buffer *yyminor, ssi_ctx_t *ctx);
|
||||
|
||||
int ssi_val_tobool(ssi_val_t *B);
|
||||
__attribute_pure__
|
||||
int ssi_val_tobool(const ssi_val_t *B);
|
||||
|
||||
__attribute_malloc__
|
||||
__attribute_returns_nonnull__
|
||||
|
|
Loading…
Reference in New Issue