#! /bin/sh /usr/share/dpatch/dpatch-run ## ngx_http_ssi_for.dpatch by Kirill A. Kroinskiy ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: Implement for/endfor command @DPATCH@ diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c index 695f901..62f2dbb 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -88,6 +88,10 @@ static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); +static ngx_int_t ngx_http_ssi_for(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); +static ngx_int_t ngx_http_ssi_endfor(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params); static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r, @@ -222,6 +226,10 @@ static ngx_str_t ngx_http_ssi_null_string = ngx_null_string; #define NGX_HTTP_SSI_BLOCK_NAME 0 +#define NGX_HTTP_SSI_FOR_VAR 0 +#define NGX_HTTP_SSI_FOR_SEP 1 +#define NGX_HTTP_SSI_FOR_DATA 2 + static ngx_http_ssi_param_t ngx_http_ssi_include_params[] = { { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 }, @@ -267,6 +275,14 @@ static ngx_http_ssi_param_t ngx_http_ssi_block_params[] = { }; +static ngx_http_ssi_param_t ngx_http_ssi_for_params[] = { + { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 0, 0 }, + { ngx_string("sep"), NGX_HTTP_SSI_FOR_SEP, 0, 0 }, + { ngx_string("data"), NGX_HTTP_SSI_FOR_DATA, 1, 0 }, + { ngx_null_string, 0, 0, 0 } +}; + + static ngx_http_ssi_param_t ngx_http_ssi_no_params[] = { { ngx_null_string, 0, 0, 0 } }; @@ -294,6 +310,9 @@ static ngx_http_ssi_command_t ngx_http_ssi_commands[] = { { ngx_string("endblock"), ngx_http_ssi_endblock, ngx_http_ssi_no_params, 0, 1, 0 }, + { ngx_string("for"), ngx_http_ssi_for, ngx_http_ssi_for_params, 0, 0, 0 }, + { ngx_string("endfor"), ngx_http_ssi_endfor, ngx_http_ssi_no_params, 0, 0, 0 }, + { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -2312,6 +2331,172 @@ ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, } +static ngx_int_t ngx_http_ssi_for(ngx_http_request_t *r, + ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) +{ + u_char *p; + u_char *last; + ngx_str_t val; + ngx_str_t *var; + ngx_str_t *sep; + ngx_str_t *data; + ngx_str_t *value; + ngx_str_t var_default = ngx_string("_"); + ngx_str_t sep_default = ngx_string(" "); + ngx_uint_t key = 0; + ngx_http_variable_value_t *vv; + + var = params[NGX_HTTP_SSI_FOR_VAR]; + sep = params[NGX_HTTP_SSI_FOR_SEP]; + data = params[NGX_HTTP_SSI_FOR_DATA]; + + if (data == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no \"data\" parameter in \"for\" SSI command"); + return NGX_HTTP_SSI_ERROR; + } + + if (!sep) { + ctx->for_sep.len = sep_default.len; + ctx->for_sep.data = ngx_palloc(r->pool, sep_default.len); + if (ctx->for_sep.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(ctx->for_sep.data, sep_default.data, sep_default.len); + + sep = &ctx->for_sep; + } else { + ctx->for_sep = *sep; + } + + + if (!var) { + ctx->for_var.len = var_default.len; + ctx->for_var.data = ngx_palloc(r->pool, var_default.len); + if (ctx->for_var.data == NULL) { + return NGX_ERROR; + } + ngx_memcpy(ctx->for_var.data, var_default.data, var_default.len); + + var = &ctx->for_var; + } else { + ctx->for_var = *var; + } + + value = ngx_http_ssi_get_variable(r, data, &key); + + if (value == NULL) { + vv = ngx_http_get_variable(r, data, key, 1); + + if (vv == NULL) { + return NGX_HTTP_SSI_ERROR; + } + + if (!vv->not_found) { + ctx->for_data.data = vv->data; + ctx->for_data.len = vv->len; + value = &ctx->for_data; + } + } else { + ctx->for_data = *value; + } + + + if (value == NULL) { + return NGX_ERROR; + } + + /* save a parser status */ + ctx->for_buf = ctx->buf; + ctx->for_pos = ctx->pos; + ctx->for_copy_start = ctx->copy_start; + ctx->for_copy_end = ctx->copy_end; + ctx->for_state = ctx->state; + ctx->for_looked = ctx->looked; + + for (p = ctx->for_data.data, last = p + ctx->for_data.len - ctx->for_sep.len; + p <= last; p++) { + if (ngx_strncmp(p, ctx->for_sep.data, ctx->for_sep.len) == 0) { + val.data = ctx->for_data.data; + val.len = p - val.data; + + ctx->for_data.data = val.data + val.len + ctx->for_sep.len; + ctx->for_data.len = ctx->for_data.len - val.len - ctx->for_sep.len; + + key = 0; + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &val) == NULL) { + return NGX_ERROR; + } + + return NGX_OK; + } + } + + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &ctx->for_data) == NULL) { + return NGX_ERROR; + } + + ctx->for_data.len = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_ssi_endfor(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, + ngx_str_t **params) +{ + u_char *p; + u_char *last; + ngx_str_t val; + ngx_uint_t key = 0; + + if (ctx->for_data.len == 0) { + return NGX_OK; + } + + for (p = ctx->for_data.data, last = p + ctx->for_data.len - ctx->for_sep.len; + p < last; p++) { + if (ngx_strncmp(p, ctx->for_sep.data, ctx->for_sep.len) == 0) { + val.data = ctx->for_data.data; + val.len = p - val.data; + + ctx->for_data.data = val.data + val.len + ctx->for_sep.len; + ctx->for_data.len = ctx->for_data.len - val.len - ctx->for_sep.len; + + key = 0; + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &val) == NULL) { + return NGX_ERROR; + } + + break; + } + } + + if (p == last) { + val = ctx->for_data; + if (ngx_strncmp(p, ctx->for_sep.data, ctx->for_sep.len) == 0) { + val.len -= ctx->for_sep.len; + } + if (ngx_http_ssi_set_variable(r, &ctx->for_var, &key, &val) == NULL) { + return NGX_ERROR; + } + + ctx->for_data.len = 0; + } + + /* revert back saved parser status */ + ctx->buf = ctx->for_buf; + ctx->pos = ctx->for_pos; + ctx->copy_start = ctx->for_copy_start; + ctx->copy_end = ctx->for_copy_end; + ctx->state = ctx->for_state; + ctx->looked = ctx->for_looked; + + return NGX_OK; +} + + static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) @@ -2798,3 +2983,10 @@ ngx_http_ssi_filter_init(ngx_conf_t *cf) return NGX_OK; } + +/* Local Variables: */ +/* mode: c */ +/* c-basic-offset: 4 */ +/* c-file-offsets: ((arglist-cont-nonempty . 4)) */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/http/modules/ngx_http_ssi_filter_module.h b/src/http/modules/ngx_http_ssi_filter_module.h index 0b84f0b..6c5d935 100644 --- a/src/http/modules/ngx_http_ssi_filter_module.h +++ b/src/http/modules/ngx_http_ssi_filter_module.h @@ -64,6 +64,18 @@ typedef struct { ngx_list_t *variables; ngx_array_t *blocks; + /* for hacks */ + ngx_buf_t *for_buf; + u_char *for_pos; + u_char *for_copy_start; + u_char *for_copy_end; + ngx_uint_t for_state; + size_t for_looked; + + ngx_str_t for_data; + ngx_str_t for_sep; + ngx_str_t for_var; + unsigned conditional:2; unsigned encoding:2; unsigned block:1; @@ -109,3 +121,10 @@ ngx_str_t *ngx_http_ssi_set_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t *key, ngx_str_t *value); #endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */ + +/* Local Variables: */ +/* mode: c */ +/* c-basic-offset: 4 */ +/* c-file-offsets: ((arglist-cont-nonempty . 4)) */ +/* indent-tabs-mode: nil */ +/* End: */