LCOV - code coverage report
Current view: top level - home/build/openresty-1.13.6.1/build/ngx_lua-0.10.11/src - ngx_http_lua_control.c (source / functions) Hit Total Coverage
Test: coverage ngix Lines: 12 219 5.5 %
Date: 2020-03-03 04:25:50 Functions: 1 6 16.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : 
       2             : /*
       3             :  * Copyright (C) Xiaozhe Wang (chaoslawful)
       4             :  * Copyright (C) Yichun Zhang (agentzh)
       5             :  */
       6             : 
       7             : 
       8             : #ifndef DDEBUG
       9             : #define DDEBUG 0
      10             : #endif
      11             : #include "ddebug.h"
      12             : 
      13             : 
      14             : #include "ngx_http_lua_control.h"
      15             : #include "ngx_http_lua_util.h"
      16             : #include "ngx_http_lua_coroutine.h"
      17             : 
      18             : 
      19             : static int ngx_http_lua_ngx_exec(lua_State *L);
      20             : static int ngx_http_lua_ngx_redirect(lua_State *L);
      21             : static int ngx_http_lua_ngx_exit(lua_State *L);
      22             : static int ngx_http_lua_on_abort(lua_State *L);
      23             : 
      24             : 
      25             : void
      26          18 : ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L)
      27             : {
      28             :     /* ngx.redirect */
      29             : 
      30          18 :     lua_pushcfunction(L, ngx_http_lua_ngx_redirect);
      31          18 :     lua_setfield(L, -2, "redirect");
      32             : 
      33             :     /* ngx.exec */
      34             : 
      35          18 :     lua_pushcfunction(L, ngx_http_lua_ngx_exec);
      36          18 :     lua_setfield(L, -2, "exec");
      37             : 
      38          18 :     lua_pushcfunction(L, ngx_http_lua_ngx_exit);
      39          18 :     lua_setfield(L, -2, "throw_error"); /* deprecated */
      40             : 
      41             :     /* ngx.exit */
      42             : 
      43          18 :     lua_pushcfunction(L, ngx_http_lua_ngx_exit);
      44          18 :     lua_setfield(L, -2, "exit");
      45             : 
      46             :     /* ngx.on_abort */
      47             : 
      48          18 :     lua_pushcfunction(L, ngx_http_lua_on_abort);
      49          18 :     lua_setfield(L, -2, "on_abort");
      50          18 : }
      51             : 
      52             : 
      53             : static int
      54           0 : ngx_http_lua_ngx_exec(lua_State *L)
      55             : {
      56             :     int                          n;
      57             :     ngx_http_request_t          *r;
      58             :     ngx_http_lua_ctx_t          *ctx;
      59             :     ngx_str_t                    uri;
      60             :     ngx_str_t                    args, user_args;
      61             :     ngx_uint_t                   flags;
      62             :     u_char                      *p;
      63             :     u_char                      *q;
      64             :     size_t                       len;
      65             :     const char                  *msg;
      66             : 
      67           0 :     n = lua_gettop(L);
      68           0 :     if (n != 1 && n != 2) {
      69           0 :         return luaL_error(L, "expecting one or two arguments, but got %d",
      70             :                           n);
      71             :     }
      72             : 
      73           0 :     r = ngx_http_lua_get_req(L);
      74           0 :     if (r == NULL) {
      75           0 :         return luaL_error(L, "no request object found");
      76             :     }
      77             : 
      78           0 :     ngx_str_null(&args);
      79             : 
      80             :     /* read the 1st argument (uri) */
      81             : 
      82           0 :     p = (u_char *) luaL_checklstring(L, 1, &len);
      83             : 
      84           0 :     if (len == 0) {
      85           0 :         return luaL_error(L, "The uri argument is empty");
      86             :     }
      87             : 
      88           0 :     uri.data = ngx_palloc(r->pool, len);
      89           0 :     if (uri.data == NULL) {
      90           0 :         return luaL_error(L, "no memory");
      91             :     }
      92             : 
      93           0 :     ngx_memcpy(uri.data, p, len);
      94             : 
      95           0 :     uri.len = len;
      96             : 
      97           0 :     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
      98           0 :     if (ctx == NULL) {
      99           0 :         return luaL_error(L, "no ctx found");
     100             :     }
     101             : 
     102           0 :     ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
     103             :                                | NGX_HTTP_LUA_CONTEXT_ACCESS
     104             :                                | NGX_HTTP_LUA_CONTEXT_CONTENT);
     105             : 
     106           0 :     ngx_http_lua_check_if_abortable(L, ctx);
     107             : 
     108           0 :     flags = NGX_HTTP_LOG_UNSAFE;
     109             : 
     110           0 :     if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
     111           0 :         return luaL_error(L, "unsafe uri");
     112             :     }
     113             : 
     114           0 :     if (n == 2) {
     115             :         /* read the 2nd argument (args) */
     116             :         dd("args type: %s", luaL_typename(L, 2));
     117             : 
     118           0 :         switch (lua_type(L, 2)) {
     119           0 :         case LUA_TNUMBER:
     120             :         case LUA_TSTRING:
     121           0 :             p = (u_char *) lua_tolstring(L, 2, &len);
     122             : 
     123           0 :             user_args.data = ngx_palloc(r->pool, len);
     124           0 :             if (user_args.data == NULL) {
     125           0 :                 return luaL_error(L, "no memory");
     126             :             }
     127             : 
     128           0 :             ngx_memcpy(user_args.data, p, len);
     129             : 
     130           0 :             user_args.len = len;
     131           0 :             break;
     132             : 
     133           0 :         case LUA_TTABLE:
     134           0 :             ngx_http_lua_process_args_option(r, L, 2, &user_args);
     135             : 
     136             :             dd("user_args: %.*s", (int) user_args.len, user_args.data);
     137             : 
     138           0 :             break;
     139             : 
     140           0 :         case LUA_TNIL:
     141           0 :             ngx_str_null(&user_args);
     142           0 :             break;
     143             : 
     144           0 :         default:
     145           0 :             msg = lua_pushfstring(L, "string, number, or table expected, "
     146             :                                   "but got %s", luaL_typename(L, 2));
     147           0 :             return luaL_argerror(L, 2, msg);
     148             :         }
     149             : 
     150             :     } else {
     151           0 :         user_args.data = NULL;
     152           0 :         user_args.len = 0;
     153             :     }
     154             : 
     155           0 :     if (user_args.len) {
     156           0 :         if (args.len == 0) {
     157           0 :             args = user_args;
     158             : 
     159             :         } else {
     160           0 :             p = ngx_palloc(r->pool, args.len + user_args.len + 1);
     161           0 :             if (p == NULL) {
     162           0 :                 return luaL_error(L, "no memory");
     163             :             }
     164             : 
     165           0 :             q = ngx_copy(p, args.data, args.len);
     166           0 :             *q++ = '&';
     167           0 :             ngx_memcpy(q, user_args.data, user_args.len);
     168             : 
     169           0 :             args.data = p;
     170           0 :             args.len += user_args.len + 1;
     171             :         }
     172             :     }
     173             : 
     174           0 :     if (r->header_sent || ctx->header_sent) {
     175           0 :         return luaL_error(L, "attempt to call ngx.exec after "
     176             :                           "sending out response headers");
     177             :     }
     178             : 
     179           0 :     ctx->exec_uri = uri;
     180           0 :     ctx->exec_args = args;
     181             : 
     182           0 :     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     183             :                    "lua exec \"%V?%V\"",
     184             :                    &ctx->exec_uri, &ctx->exec_args);
     185             : 
     186           0 :     return lua_yield(L, 0);
     187             : }
     188             : 
     189             : 
     190             : static int
     191           0 : ngx_http_lua_ngx_redirect(lua_State *L)
     192             : {
     193             :     ngx_http_lua_ctx_t          *ctx;
     194             :     ngx_int_t                    rc;
     195             :     int                          n;
     196             :     u_char                      *p;
     197             :     u_char                      *uri;
     198             :     size_t                       len;
     199             :     ngx_table_elt_t             *h;
     200             :     ngx_http_request_t          *r;
     201             : 
     202           0 :     n = lua_gettop(L);
     203             : 
     204           0 :     if (n != 1 && n != 2) {
     205           0 :         return luaL_error(L, "expecting one or two arguments");
     206             :     }
     207             : 
     208           0 :     p = (u_char *) luaL_checklstring(L, 1, &len);
     209             : 
     210           0 :     if (n == 2) {
     211           0 :         rc = (ngx_int_t) luaL_checknumber(L, 2);
     212             : 
     213           0 :         if (rc != NGX_HTTP_MOVED_TEMPORARILY
     214           0 :             && rc != NGX_HTTP_MOVED_PERMANENTLY
     215           0 :             && rc != NGX_HTTP_SEE_OTHER
     216           0 :             && rc != NGX_HTTP_TEMPORARY_REDIRECT)
     217             :         {
     218           0 :             return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY, "
     219             :                               "ngx.HTTP_MOVED_PERMANENTLY, "
     220             :                               "ngx.HTTP_SEE_OTHER, and "
     221             :                               "ngx.HTTP_TEMPORARY_REDIRECT are allowed");
     222             :         }
     223             : 
     224             :     } else {
     225           0 :         rc = NGX_HTTP_MOVED_TEMPORARILY;
     226             :     }
     227             : 
     228           0 :     r = ngx_http_lua_get_req(L);
     229           0 :     if (r == NULL) {
     230           0 :         return luaL_error(L, "no request object found");
     231             :     }
     232             : 
     233           0 :     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
     234           0 :     if (ctx == NULL) {
     235           0 :         return luaL_error(L, "no request ctx found");
     236             :     }
     237             : 
     238           0 :     ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
     239             :                                | NGX_HTTP_LUA_CONTEXT_ACCESS
     240             :                                | NGX_HTTP_LUA_CONTEXT_CONTENT);
     241             : 
     242           0 :     ngx_http_lua_check_if_abortable(L, ctx);
     243             : 
     244           0 :     if (r->header_sent || ctx->header_sent) {
     245           0 :         return luaL_error(L, "attempt to call ngx.redirect after sending out "
     246             :                           "the headers");
     247             :     }
     248             : 
     249           0 :     uri = ngx_palloc(r->pool, len);
     250           0 :     if (uri == NULL) {
     251           0 :         return luaL_error(L, "no memory");
     252             :     }
     253             : 
     254           0 :     ngx_memcpy(uri, p, len);
     255             : 
     256           0 :     h = ngx_list_push(&r->headers_out.headers);
     257           0 :     if (h == NULL) {
     258           0 :         return luaL_error(L, "no memory");
     259             :     }
     260             : 
     261           0 :     h->hash = ngx_http_lua_location_hash;
     262             : 
     263             : #if 0
     264             :     dd("location hash: %lu == %lu",
     265             :        (unsigned long) h->hash,
     266             :        (unsigned long) ngx_hash_key_lc((u_char *) "Location",
     267             :                                        sizeof("Location") - 1));
     268             : #endif
     269             : 
     270           0 :     h->value.len = len;
     271           0 :     h->value.data = uri;
     272           0 :     ngx_str_set(&h->key, "Location");
     273             : 
     274           0 :     r->headers_out.status = rc;
     275             : 
     276           0 :     ctx->exit_code = rc;
     277           0 :     ctx->exited = 1;
     278             : 
     279           0 :     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     280             :                    "lua redirect to \"%V\" with code %i",
     281             :                    &h->value, ctx->exit_code);
     282             : 
     283           0 :     if (len && uri[0] != '/') {
     284           0 :         r->headers_out.location = h;
     285             :     }
     286             : 
     287             :     /*
     288             :      * we do not set r->headers_out.location here to avoid the handling
     289             :      * the local redirects without a host name by ngx_http_header_filter()
     290             :      */
     291             : 
     292           0 :     return lua_yield(L, 0);
     293             : }
     294             : 
     295             : 
     296             : static int
     297           0 : ngx_http_lua_ngx_exit(lua_State *L)
     298             : {
     299             :     ngx_int_t                    rc;
     300             :     ngx_http_request_t          *r;
     301             :     ngx_http_lua_ctx_t          *ctx;
     302             : 
     303           0 :     if (lua_gettop(L) != 1) {
     304           0 :         return luaL_error(L, "expecting one argument");
     305             :     }
     306             : 
     307           0 :     r = ngx_http_lua_get_req(L);
     308           0 :     if (r == NULL) {
     309           0 :         return luaL_error(L, "no request object found");
     310             :     }
     311             : 
     312           0 :     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
     313           0 :     if (ctx == NULL) {
     314           0 :         return luaL_error(L, "no request ctx found");
     315             :     }
     316             : 
     317           0 :     ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
     318             :                                | NGX_HTTP_LUA_CONTEXT_ACCESS
     319             :                                | NGX_HTTP_LUA_CONTEXT_CONTENT
     320             :                                | NGX_HTTP_LUA_CONTEXT_TIMER
     321             :                                | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
     322             :                                | NGX_HTTP_LUA_CONTEXT_BALANCER
     323             :                                | NGX_HTTP_LUA_CONTEXT_SSL_CERT
     324             :                                | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
     325             :                                | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH);
     326             : 
     327           0 :     rc = (ngx_int_t) luaL_checkinteger(L, 1);
     328             : 
     329           0 :     if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
     330             :                         | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
     331             :                         | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))
     332             :     {
     333             : 
     334             : #if (NGX_HTTP_SSL)
     335             : 
     336           0 :         ctx->exit_code = rc;
     337           0 :         ctx->exited = 1;
     338             : 
     339           0 :         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     340             :                        "lua exit with code %i", rc);
     341             : 
     342           0 :         if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) {
     343           0 :             return 0;
     344             :         }
     345             : 
     346           0 :         return lua_yield(L, 0);
     347             : 
     348             : #else
     349             : 
     350             :         return luaL_error(L, "no SSL support");
     351             : 
     352             : #endif
     353             :     }
     354             : 
     355           0 :     if (ctx->no_abort
     356           0 :         && rc != NGX_ERROR
     357           0 :         && rc != NGX_HTTP_CLOSE
     358           0 :         && rc != NGX_HTTP_REQUEST_TIME_OUT
     359           0 :         && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST)
     360             :     {
     361           0 :         return luaL_error(L, "attempt to abort with pending subrequests");
     362             :     }
     363             : 
     364           0 :     if ((r->header_sent || ctx->header_sent)
     365           0 :         && rc >= NGX_HTTP_SPECIAL_RESPONSE
     366           0 :         && rc != NGX_HTTP_REQUEST_TIME_OUT
     367           0 :         && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST
     368           0 :         && rc != NGX_HTTP_CLOSE)
     369             :     {
     370           0 :         if (rc != (ngx_int_t) r->headers_out.status) {
     371           0 :             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to "
     372             :                           "set status %i via ngx.exit after sending out the "
     373             :                           "response status %ui", rc, r->headers_out.status);
     374             :         }
     375             : 
     376           0 :         rc = NGX_HTTP_OK;
     377             :     }
     378             : 
     379             :     dd("setting exit code: %d", (int) rc);
     380             : 
     381           0 :     ctx->exit_code = rc;
     382           0 :     ctx->exited = 1;
     383             : 
     384           0 :     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     385             :                    "lua exit with code %i", ctx->exit_code);
     386             : 
     387           0 :     if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
     388             :                         | NGX_HTTP_LUA_CONTEXT_BALANCER))
     389             :     {
     390           0 :         return 0;
     391             :     }
     392             : 
     393             :     dd("calling yield");
     394           0 :     return lua_yield(L, 0);
     395             : }
     396             : 
     397             : 
     398             : static int
     399           0 : ngx_http_lua_on_abort(lua_State *L)
     400             : {
     401             :     ngx_http_request_t           *r;
     402             :     ngx_http_lua_ctx_t           *ctx;
     403           0 :     ngx_http_lua_co_ctx_t        *coctx = NULL;
     404             :     ngx_http_lua_loc_conf_t      *llcf;
     405             : 
     406           0 :     r = ngx_http_lua_get_req(L);
     407           0 :     if (r == NULL) {
     408           0 :         return luaL_error(L, "no request found");
     409             :     }
     410             : 
     411           0 :     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
     412           0 :     if (ctx == NULL) {
     413           0 :         return luaL_error(L, "no request ctx found");
     414             :     }
     415             : 
     416           0 :     ngx_http_lua_check_fake_request2(L, r, ctx);
     417             : 
     418           0 :     if (ctx->on_abort_co_ctx) {
     419           0 :         lua_pushnil(L);
     420           0 :         lua_pushliteral(L, "duplicate call");
     421           0 :         return 2;
     422             :     }
     423             : 
     424           0 :     llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
     425           0 :     if (!llcf->check_client_abort) {
     426           0 :         lua_pushnil(L);
     427           0 :         lua_pushliteral(L, "lua_check_client_abort is off");
     428           0 :         return 2;
     429             :     }
     430             : 
     431           0 :     ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx);
     432             : 
     433           0 :     lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key);
     434           0 :     lua_rawget(L, LUA_REGISTRYINDEX);
     435           0 :     lua_pushvalue(L, -2);
     436             : 
     437             :     dd("on_wait thread 1: %p", lua_tothread(L, -1));
     438             : 
     439           0 :     coctx->co_ref = luaL_ref(L, -2);
     440           0 :     lua_pop(L, 1);
     441             : 
     442           0 :     coctx->is_uthread = 1;
     443           0 :     ctx->on_abort_co_ctx = coctx;
     444             : 
     445             :     dd("on_wait thread 2: %p", coctx->co);
     446             : 
     447           0 :     coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED;
     448           0 :     coctx->parent_co_ctx = ctx->cur_co_ctx;
     449             : 
     450           0 :     lua_pushinteger(L, 1);
     451           0 :     return 1;
     452             : }
     453             : 
     454             : 
     455             : #ifndef NGX_LUA_NO_FFI_API
     456             : int
     457           0 : ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err,
     458             :     size_t *errlen)
     459             : {
     460             :     ngx_http_lua_ctx_t       *ctx;
     461             : 
     462           0 :     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
     463           0 :     if (ctx == NULL) {
     464           0 :         *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err;
     465           0 :         return NGX_ERROR;
     466             :     }
     467             : 
     468           0 :     if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE
     469             :                                        | NGX_HTTP_LUA_CONTEXT_ACCESS
     470             :                                        | NGX_HTTP_LUA_CONTEXT_CONTENT
     471             :                                        | NGX_HTTP_LUA_CONTEXT_TIMER
     472             :                                        | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
     473             :                                        | NGX_HTTP_LUA_CONTEXT_BALANCER
     474             :                                        | NGX_HTTP_LUA_CONTEXT_SSL_CERT
     475             :                                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
     476             :                                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH,
     477             :                                        err, errlen)
     478             :         != NGX_OK)
     479             :     {
     480           0 :         return NGX_ERROR;
     481             :     }
     482             : 
     483           0 :     if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
     484             :                         | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
     485             :                         | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))
     486             :     {
     487             : 
     488             : #if (NGX_HTTP_SSL)
     489             : 
     490           0 :         ctx->exit_code = status;
     491           0 :         ctx->exited = 1;
     492             : 
     493           0 :         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     494             :                        "lua exit with code %d", status);
     495             : 
     496           0 :         if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) {
     497           0 :             return NGX_DONE;
     498             :         }
     499             : 
     500           0 :         return NGX_OK;
     501             : 
     502             : #else
     503             : 
     504             :         return NGX_ERROR;
     505             : 
     506             : #endif
     507             :     }
     508             : 
     509           0 :     if (ctx->no_abort
     510           0 :         && status != NGX_ERROR
     511           0 :         && status != NGX_HTTP_CLOSE
     512           0 :         && status != NGX_HTTP_REQUEST_TIME_OUT
     513           0 :         && status != NGX_HTTP_CLIENT_CLOSED_REQUEST)
     514             :     {
     515           0 :         *errlen = ngx_snprintf(err, *errlen,
     516             :                                "attempt to abort with pending subrequests")
     517           0 :                   - err;
     518           0 :         return NGX_ERROR;
     519             :     }
     520             : 
     521           0 :     if ((r->header_sent || ctx->header_sent)
     522           0 :         && status >= NGX_HTTP_SPECIAL_RESPONSE
     523           0 :         && status != NGX_HTTP_REQUEST_TIME_OUT
     524           0 :         && status != NGX_HTTP_CLIENT_CLOSED_REQUEST
     525           0 :         && status != NGX_HTTP_CLOSE)
     526             :     {
     527           0 :         if (status != (ngx_int_t) r->headers_out.status) {
     528           0 :             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to "
     529             :                           "set status %d via ngx.exit after sending out the "
     530             :                           "response status %ui", status,
     531             :                           r->headers_out.status);
     532             :         }
     533             : 
     534           0 :         status = NGX_HTTP_OK;
     535             :     }
     536             : 
     537           0 :     ctx->exit_code = status;
     538           0 :     ctx->exited = 1;
     539             : 
     540           0 :     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
     541             :                    "lua exit with code %i", ctx->exit_code);
     542             : 
     543           0 :     if (ctx->context & (NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
     544             :                         | NGX_HTTP_LUA_CONTEXT_BALANCER))
     545             :     {
     546           0 :         return NGX_DONE;
     547             :     }
     548             : 
     549           0 :     return NGX_OK;
     550             : }
     551             : #endif  /* NGX_LUA_NO_FFI_API */
     552             : 
     553             : /* vi:set ft=c ts=4 sw=4 et fdm=marker: */

Generated by: LCOV version 1.13