Return to php_functions.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / sapi / apache2handler |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 The PHP Group | ! 6: +----------------------------------------------------------------------+ ! 7: | This source file is subject to version 3.01 of the PHP license, | ! 8: | that is bundled with this package in the file LICENSE, and is | ! 9: | available through the world-wide-web at the following url: | ! 10: | http://www.php.net/license/3_01.txt | ! 11: | If you did not receive a copy of the PHP license and are unable to | ! 12: | obtain it through the world-wide-web, please send a note to | ! 13: | license@php.net so we can mail you a copy immediately. | ! 14: +----------------------------------------------------------------------+ ! 15: | Author: Sascha Schumann <sascha@schumann.cx> | ! 16: +----------------------------------------------------------------------+ ! 17: */ ! 18: ! 19: /* $Id: php_functions.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 20: ! 21: #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS ! 22: ! 23: #include "php.h" ! 24: #include "ext/standard/php_smart_str.h" ! 25: #include "ext/standard/info.h" ! 26: #include "ext/standard/head.h" ! 27: #include "php_ini.h" ! 28: #include "SAPI.h" ! 29: ! 30: #define CORE_PRIVATE ! 31: #include "apr_strings.h" ! 32: #include "apr_time.h" ! 33: #include "ap_config.h" ! 34: #include "util_filter.h" ! 35: #include "httpd.h" ! 36: #include "http_config.h" ! 37: #include "http_request.h" ! 38: #include "http_core.h" ! 39: #include "http_protocol.h" ! 40: #include "http_log.h" ! 41: #include "http_main.h" ! 42: #include "util_script.h" ! 43: #include "http_core.h" ! 44: #include "ap_mpm.h" ! 45: #if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE) ! 46: #include "unixd.h" ! 47: #endif ! 48: ! 49: #include "php_apache.h" ! 50: ! 51: #ifdef ZTS ! 52: int php_apache2_info_id; ! 53: #else ! 54: php_apache2_info_struct php_apache2_info; ! 55: #endif ! 56: ! 57: #define SECTION(name) PUTS("<h2>" name "</h2>\n") ! 58: ! 59: static request_rec *php_apache_lookup_uri(char *filename TSRMLS_DC) ! 60: { ! 61: php_struct *ctx = SG(server_context); ! 62: ! 63: if (!filename || !ctx || !ctx->r) { ! 64: return NULL; ! 65: } ! 66: ! 67: return ap_sub_req_lookup_uri(filename, ctx->r, ctx->r->output_filters); ! 68: } ! 69: ! 70: /* {{{ proto bool virtual(string uri) ! 71: Perform an apache sub-request */ ! 72: PHP_FUNCTION(virtual) ! 73: { ! 74: char *filename; ! 75: int filename_len; ! 76: request_rec *rr; ! 77: ! 78: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { ! 79: return; ! 80: } ! 81: ! 82: if (!(rr = php_apache_lookup_uri(filename TSRMLS_CC))) { ! 83: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename); ! 84: RETURN_FALSE; ! 85: } ! 86: ! 87: if (rr->status != HTTP_OK) { ! 88: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename); ! 89: ap_destroy_sub_req(rr); ! 90: RETURN_FALSE; ! 91: } ! 92: ! 93: /* Flush everything. */ ! 94: php_end_ob_buffers(1 TSRMLS_CC); ! 95: php_header(TSRMLS_C); ! 96: ! 97: /* Ensure that the ap_r* layer for the main request is flushed, to ! 98: * work around http://issues.apache.org/bugzilla/show_bug.cgi?id=17629 */ ! 99: ap_rflush(rr->main); ! 100: ! 101: if (ap_run_sub_req(rr)) { ! 102: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - request execution failed", filename); ! 103: ap_destroy_sub_req(rr); ! 104: RETURN_FALSE; ! 105: } ! 106: ap_destroy_sub_req(rr); ! 107: RETURN_TRUE; ! 108: } ! 109: /* }}} */ ! 110: ! 111: #define ADD_LONG(name) \ ! 112: add_property_long(return_value, #name, rr->name) ! 113: #define ADD_TIME(name) \ ! 114: add_property_long(return_value, #name, apr_time_sec(rr->name)); ! 115: #define ADD_STRING(name) \ ! 116: if (rr->name) add_property_string(return_value, #name, (char *) rr->name, 1) ! 117: ! 118: PHP_FUNCTION(apache_lookup_uri) ! 119: { ! 120: request_rec *rr; ! 121: char *filename; ! 122: int filename_len; ! 123: ! 124: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { ! 125: return; ! 126: } ! 127: ! 128: if (!(rr = php_apache_lookup_uri(filename TSRMLS_CC))) { ! 129: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename); ! 130: RETURN_FALSE; ! 131: } ! 132: ! 133: if (rr->status == HTTP_OK) { ! 134: object_init(return_value); ! 135: ! 136: ADD_LONG(status); ! 137: ADD_STRING(the_request); ! 138: ADD_STRING(status_line); ! 139: ADD_STRING(method); ! 140: ADD_TIME(mtime); ! 141: ADD_LONG(clength); ! 142: #if MODULE_MAGIC_NUMBER < 20020506 ! 143: ADD_STRING(boundary); ! 144: #endif ! 145: ADD_STRING(range); ! 146: ADD_LONG(chunked); ! 147: ADD_STRING(content_type); ! 148: ADD_STRING(handler); ! 149: ADD_LONG(no_cache); ! 150: ADD_LONG(no_local_copy); ! 151: ADD_STRING(unparsed_uri); ! 152: ADD_STRING(uri); ! 153: ADD_STRING(filename); ! 154: ADD_STRING(path_info); ! 155: ADD_STRING(args); ! 156: ADD_LONG(allowed); ! 157: ADD_LONG(sent_bodyct); ! 158: ADD_LONG(bytes_sent); ! 159: ADD_LONG(mtime); ! 160: ADD_TIME(request_time); ! 161: ! 162: ap_destroy_sub_req(rr); ! 163: return; ! 164: } ! 165: ! 166: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename); ! 167: ap_destroy_sub_req(rr); ! 168: RETURN_FALSE; ! 169: } ! 170: ! 171: /* {{{ proto array getallheaders(void) ! 172: Fetch all HTTP request headers */ ! 173: PHP_FUNCTION(apache_request_headers) ! 174: { ! 175: php_struct *ctx; ! 176: const apr_array_header_t *arr; ! 177: char *key, *val; ! 178: ! 179: if (zend_parse_parameters_none() == FAILURE) { ! 180: return; ! 181: } ! 182: ! 183: array_init(return_value); ! 184: ! 185: ctx = SG(server_context); ! 186: arr = apr_table_elts(ctx->r->headers_in); ! 187: ! 188: APR_ARRAY_FOREACH_OPEN(arr, key, val) ! 189: if (!val) val = ""; ! 190: add_assoc_string(return_value, key, val, 1); ! 191: APR_ARRAY_FOREACH_CLOSE() ! 192: } ! 193: /* }}} */ ! 194: ! 195: /* {{{ proto array apache_response_headers(void) ! 196: Fetch all HTTP response headers */ ! 197: PHP_FUNCTION(apache_response_headers) ! 198: { ! 199: php_struct *ctx; ! 200: const apr_array_header_t *arr; ! 201: char *key, *val; ! 202: ! 203: if (zend_parse_parameters_none() == FAILURE) { ! 204: return; ! 205: } ! 206: ! 207: array_init(return_value); ! 208: ! 209: ctx = SG(server_context); ! 210: arr = apr_table_elts(ctx->r->headers_out); ! 211: ! 212: APR_ARRAY_FOREACH_OPEN(arr, key, val) ! 213: if (!val) val = ""; ! 214: add_assoc_string(return_value, key, val, 1); ! 215: APR_ARRAY_FOREACH_CLOSE() ! 216: } ! 217: /* }}} */ ! 218: ! 219: /* {{{ proto string apache_note(string note_name [, string note_value]) ! 220: Get and set Apache request notes */ ! 221: PHP_FUNCTION(apache_note) ! 222: { ! 223: php_struct *ctx; ! 224: char *note_name, *note_val = NULL; ! 225: int note_name_len, note_val_len; ! 226: char *old_note_val=NULL; ! 227: ! 228: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", ¬e_name, ¬e_name_len, ¬e_val, ¬e_val_len) == FAILURE) { ! 229: return; ! 230: } ! 231: ! 232: ctx = SG(server_context); ! 233: ! 234: old_note_val = (char *) apr_table_get(ctx->r->notes, note_name); ! 235: ! 236: if (note_val) { ! 237: apr_table_set(ctx->r->notes, note_name, note_val); ! 238: } ! 239: ! 240: if (old_note_val) { ! 241: RETURN_STRING(old_note_val, 1); ! 242: } ! 243: ! 244: RETURN_FALSE; ! 245: } ! 246: /* }}} */ ! 247: ! 248: ! 249: /* {{{ proto bool apache_setenv(string variable, string value [, bool walk_to_top]) ! 250: Set an Apache subprocess_env variable */ ! 251: /* ! 252: * XXX this doesn't look right. shouldn't it be the parent ?*/ ! 253: PHP_FUNCTION(apache_setenv) ! 254: { ! 255: php_struct *ctx; ! 256: char *variable=NULL, *string_val=NULL; ! 257: int variable_len, string_val_len; ! 258: zend_bool walk_to_top = 0; ! 259: int arg_count = ZEND_NUM_ARGS(); ! 260: request_rec *r; ! 261: ! 262: if (zend_parse_parameters(arg_count TSRMLS_CC, "ss|b", &variable, &variable_len, &string_val, &string_val_len, &walk_to_top) == FAILURE) { ! 263: return; ! 264: } ! 265: ! 266: ctx = SG(server_context); ! 267: ! 268: r = ctx->r; ! 269: if (arg_count == 3) { ! 270: if (walk_to_top) { ! 271: while(r->prev) { ! 272: r = r->prev; ! 273: } ! 274: } ! 275: } ! 276: ! 277: apr_table_set(r->subprocess_env, variable, string_val); ! 278: ! 279: RETURN_TRUE; ! 280: } ! 281: /* }}} */ ! 282: ! 283: /* {{{ proto bool apache_getenv(string variable [, bool walk_to_top]) ! 284: Get an Apache subprocess_env variable */ ! 285: /* ! 286: * XXX: shouldn't this be the parent not the 'prev' ! 287: */ ! 288: PHP_FUNCTION(apache_getenv) ! 289: { ! 290: php_struct *ctx; ! 291: char *variable=NULL; ! 292: int variable_len; ! 293: zend_bool walk_to_top = 0; ! 294: int arg_count = ZEND_NUM_ARGS(); ! 295: char *env_val=NULL; ! 296: request_rec *r; ! 297: ! 298: if (zend_parse_parameters(arg_count TSRMLS_CC, "s|b", &variable, &variable_len, &walk_to_top) == FAILURE) { ! 299: return; ! 300: } ! 301: ! 302: ctx = SG(server_context); ! 303: ! 304: r = ctx->r; ! 305: if (arg_count == 2) { ! 306: if (walk_to_top) { ! 307: while(r->prev) { ! 308: r = r->prev; ! 309: } ! 310: } ! 311: } ! 312: ! 313: env_val = (char*) apr_table_get(r->subprocess_env, variable); ! 314: ! 315: if (env_val != NULL) { ! 316: RETURN_STRING(env_val, 1); ! 317: } ! 318: ! 319: RETURN_FALSE; ! 320: } ! 321: /* }}} */ ! 322: ! 323: static char *php_apache_get_version() ! 324: { ! 325: #if MODULE_MAGIC_NUMBER_MAJOR >= 20060905 ! 326: return (char *) ap_get_server_banner(); ! 327: #else ! 328: return (char *) ap_get_server_version(); ! 329: #endif ! 330: } ! 331: ! 332: /* {{{ proto string apache_get_version(void) ! 333: Fetch Apache version */ ! 334: PHP_FUNCTION(apache_get_version) ! 335: { ! 336: char *apv = php_apache_get_version(); ! 337: ! 338: if (apv && *apv) { ! 339: RETURN_STRING(apv, 1); ! 340: } else { ! 341: RETURN_FALSE; ! 342: } ! 343: } ! 344: /* }}} */ ! 345: ! 346: /* {{{ proto array apache_get_modules(void) ! 347: Get a list of loaded Apache modules */ ! 348: PHP_FUNCTION(apache_get_modules) ! 349: { ! 350: int n; ! 351: char *p; ! 352: ! 353: array_init(return_value); ! 354: ! 355: for (n = 0; ap_loaded_modules[n]; ++n) { ! 356: char *s = (char *) ap_loaded_modules[n]->name; ! 357: if ((p = strchr(s, '.'))) { ! 358: add_next_index_stringl(return_value, s, (p - s), 1); ! 359: } else { ! 360: add_next_index_string(return_value, s, 1); ! 361: } ! 362: } ! 363: } ! 364: /* }}} */ ! 365: ! 366: PHP_MINFO_FUNCTION(apache) ! 367: { ! 368: char *apv = php_apache_get_version(); ! 369: smart_str tmp1 = {0}; ! 370: char tmp[1024]; ! 371: int n, max_requests; ! 372: char *p; ! 373: server_rec *serv = ((php_struct *) SG(server_context))->r->server; ! 374: #if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE) ! 375: #if MODULE_MAGIC_NUMBER_MAJOR >= 20081201 ! 376: AP_DECLARE_DATA extern unixd_config_rec ap_unixd_config; ! 377: #else ! 378: AP_DECLARE_DATA extern unixd_config_rec unixd_config; ! 379: #endif ! 380: #endif ! 381: ! 382: for (n = 0; ap_loaded_modules[n]; ++n) { ! 383: char *s = (char *) ap_loaded_modules[n]->name; ! 384: if ((p = strchr(s, '.'))) { ! 385: smart_str_appendl(&tmp1, s, (p - s)); ! 386: } else { ! 387: smart_str_appends(&tmp1, s); ! 388: } ! 389: smart_str_appendc(&tmp1, ' '); ! 390: } ! 391: if ((tmp1.len - 1) >= 0) { ! 392: tmp1.c[tmp1.len - 1] = '\0'; ! 393: } ! 394: ! 395: php_info_print_table_start(); ! 396: if (apv && *apv) { ! 397: php_info_print_table_row(2, "Apache Version", apv); ! 398: } ! 399: snprintf(tmp, sizeof(tmp), "%d", MODULE_MAGIC_NUMBER); ! 400: php_info_print_table_row(2, "Apache API Version", tmp); ! 401: ! 402: if (serv->server_admin && *(serv->server_admin)) { ! 403: php_info_print_table_row(2, "Server Administrator", serv->server_admin); ! 404: } ! 405: ! 406: snprintf(tmp, sizeof(tmp), "%s:%u", serv->server_hostname, serv->port); ! 407: php_info_print_table_row(2, "Hostname:Port", tmp); ! 408: ! 409: #if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE) ! 410: #if MODULE_MAGIC_NUMBER_MAJOR >= 20081201 ! 411: snprintf(tmp, sizeof(tmp), "%s(%d)/%d", ap_unixd_config.user_name, ap_unixd_config.user_id, ap_unixd_config.group_id); ! 412: #else ! 413: snprintf(tmp, sizeof(tmp), "%s(%d)/%d", unixd_config.user_name, unixd_config.user_id, unixd_config.group_id); ! 414: #endif ! 415: php_info_print_table_row(2, "User/Group", tmp); ! 416: #endif ! 417: ! 418: ap_mpm_query(AP_MPMQ_MAX_REQUESTS_DAEMON, &max_requests); ! 419: snprintf(tmp, sizeof(tmp), "Per Child: %d - Keep Alive: %s - Max Per Connection: %d", max_requests, (serv->keep_alive ? "on":"off"), serv->keep_alive_max); ! 420: php_info_print_table_row(2, "Max Requests", tmp); ! 421: ! 422: apr_snprintf(tmp, sizeof tmp, ! 423: "Connection: %" APR_TIME_T_FMT " - Keep-Alive: %" APR_TIME_T_FMT, ! 424: apr_time_sec(serv->timeout), apr_time_sec(serv->keep_alive_timeout)); ! 425: php_info_print_table_row(2, "Timeouts", tmp); ! 426: ! 427: php_info_print_table_row(2, "Virtual Server", (serv->is_virtual ? "Yes" : "No")); ! 428: php_info_print_table_row(2, "Server Root", ap_server_root); ! 429: php_info_print_table_row(2, "Loaded Modules", tmp1.c); ! 430: ! 431: smart_str_free(&tmp1); ! 432: php_info_print_table_end(); ! 433: ! 434: DISPLAY_INI_ENTRIES(); ! 435: ! 436: { ! 437: const apr_array_header_t *arr = apr_table_elts(((php_struct *) SG(server_context))->r->subprocess_env); ! 438: char *key, *val; ! 439: ! 440: SECTION("Apache Environment"); ! 441: php_info_print_table_start(); ! 442: php_info_print_table_header(2, "Variable", "Value"); ! 443: APR_ARRAY_FOREACH_OPEN(arr, key, val) ! 444: if (!val) { ! 445: val = ""; ! 446: } ! 447: php_info_print_table_row(2, key, val); ! 448: APR_ARRAY_FOREACH_CLOSE() ! 449: ! 450: php_info_print_table_end(); ! 451: ! 452: SECTION("HTTP Headers Information"); ! 453: php_info_print_table_start(); ! 454: php_info_print_table_colspan_header(2, "HTTP Request Headers"); ! 455: php_info_print_table_row(2, "HTTP Request", ((php_struct *) SG(server_context))->r->the_request); ! 456: ! 457: arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_in); ! 458: APR_ARRAY_FOREACH_OPEN(arr, key, val) ! 459: if (!val) { ! 460: val = ""; ! 461: } ! 462: php_info_print_table_row(2, key, val); ! 463: APR_ARRAY_FOREACH_CLOSE() ! 464: ! 465: php_info_print_table_colspan_header(2, "HTTP Response Headers"); ! 466: arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_out); ! 467: APR_ARRAY_FOREACH_OPEN(arr, key, val) ! 468: if (!val) { ! 469: val = ""; ! 470: } ! 471: php_info_print_table_row(2, key, val); ! 472: APR_ARRAY_FOREACH_CLOSE() ! 473: ! 474: php_info_print_table_end(); ! 475: } ! 476: } ! 477: ! 478: /* {{{ arginfo */ ! 479: ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_lookup_uri, 0, 0, 1) ! 480: ZEND_ARG_INFO(0, filename) ! 481: ZEND_END_ARG_INFO() ! 482: ! 483: ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_virtual, 0, 0, 1) ! 484: ZEND_ARG_INFO(0, uri) ! 485: ZEND_END_ARG_INFO() ! 486: ! 487: ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_response_headers, 0) ! 488: ZEND_END_ARG_INFO() ! 489: ! 490: ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_getallheaders, 0) ! 491: ZEND_END_ARG_INFO() ! 492: ! 493: ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_note, 0, 0, 1) ! 494: ZEND_ARG_INFO(0, note_name) ! 495: ZEND_ARG_INFO(0, note_value) ! 496: ZEND_END_ARG_INFO() ! 497: ! 498: ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_setenv, 0, 0, 2) ! 499: ZEND_ARG_INFO(0, variable) ! 500: ZEND_ARG_INFO(0, value) ! 501: ZEND_ARG_INFO(0, walk_to_top) ! 502: ZEND_END_ARG_INFO() ! 503: ! 504: ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_getenv, 0, 0, 1) ! 505: ZEND_ARG_INFO(0, variable) ! 506: ZEND_ARG_INFO(0, walk_to_top) ! 507: ZEND_END_ARG_INFO() ! 508: ! 509: ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_get_version, 0) ! 510: ZEND_END_ARG_INFO() ! 511: ! 512: ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_get_modules, 0) ! 513: ZEND_END_ARG_INFO() ! 514: /* }}} */ ! 515: ! 516: static const zend_function_entry apache_functions[] = { ! 517: PHP_FE(apache_lookup_uri, arginfo_apache2handler_lookup_uri) ! 518: PHP_FE(virtual, arginfo_apache2handler_virtual) ! 519: PHP_FE(apache_request_headers, arginfo_apache2handler_getallheaders) ! 520: PHP_FE(apache_response_headers, arginfo_apache2handler_response_headers) ! 521: PHP_FE(apache_setenv, arginfo_apache2handler_setenv) ! 522: PHP_FE(apache_getenv, arginfo_apache2handler_getenv) ! 523: PHP_FE(apache_note, arginfo_apache2handler_note) ! 524: PHP_FE(apache_get_version, arginfo_apache2handler_get_version) ! 525: PHP_FE(apache_get_modules, arginfo_apache2handler_get_modules) ! 526: PHP_FALIAS(getallheaders, apache_request_headers, arginfo_apache2handler_getallheaders) ! 527: {NULL, NULL, NULL} ! 528: }; ! 529: ! 530: PHP_INI_BEGIN() ! 531: STD_PHP_INI_ENTRY("xbithack", "0", PHP_INI_ALL, OnUpdateLong, xbithack, php_apache2_info_struct, php_apache2_info) ! 532: STD_PHP_INI_ENTRY("engine", "1", PHP_INI_ALL, OnUpdateLong, engine, php_apache2_info_struct, php_apache2_info) ! 533: STD_PHP_INI_ENTRY("last_modified", "0", PHP_INI_ALL, OnUpdateLong, last_modified, php_apache2_info_struct, php_apache2_info) ! 534: PHP_INI_END() ! 535: ! 536: static PHP_MINIT_FUNCTION(apache) ! 537: { ! 538: #ifdef ZTS ! 539: ts_allocate_id(&php_apache2_info_id, sizeof(php_apache2_info_struct), (ts_allocate_ctor) NULL, NULL); ! 540: #endif ! 541: REGISTER_INI_ENTRIES(); ! 542: return SUCCESS; ! 543: } ! 544: ! 545: static PHP_MSHUTDOWN_FUNCTION(apache) ! 546: { ! 547: UNREGISTER_INI_ENTRIES(); ! 548: return SUCCESS; ! 549: } ! 550: ! 551: zend_module_entry php_apache_module = { ! 552: STANDARD_MODULE_HEADER, ! 553: "apache2handler", ! 554: apache_functions, ! 555: PHP_MINIT(apache), ! 556: PHP_MSHUTDOWN(apache), ! 557: NULL, ! 558: NULL, ! 559: PHP_MINFO(apache), ! 560: NULL, ! 561: STANDARD_MODULE_PROPERTIES ! 562: }; ! 563: ! 564: /* ! 565: * Local variables: ! 566: * tab-width: 4 ! 567: * c-basic-offset: 4 ! 568: * End: ! 569: * vim600: sw=4 ts=4 fdm=marker ! 570: * vim<600: sw=4 ts=4 ! 571: */