2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2011, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 static php_http_params_token_t def_param_sep
= {",", 1}, *def_param_sep_ptr
[] = {&def_param_sep
, NULL
};
16 static php_http_params_token_t def_arg_sep
= {";", 1}, *def_arg_sep_ptr
[] = {&def_arg_sep
, NULL
};
17 static php_http_params_token_t def_val_sep
= {"=", 1}, *def_val_sep_ptr
[] = {&def_val_sep
, NULL
};
18 static php_http_params_opts_t def_opts
= {
19 .param
= def_param_sep_ptr
,
20 .arg
= def_arg_sep_ptr
,
21 .val
= def_val_sep_ptr
24 PHP_HTTP_API php_http_params_opts_t
*php_http_params_opts_default_get(php_http_params_opts_t
*opts
)
27 opts
= emalloc(sizeof(*opts
));
30 memcpy(opts
, &def_opts
, sizeof(def_opts
));
35 typedef struct php_http_params_state
{
36 php_http_params_token_t input
;
37 php_http_params_token_t param
;
38 php_http_params_token_t arg
;
39 php_http_params_token_t val
;
45 } php_http_params_state_t
;
47 static inline void sanitize_string(char *str
, size_t len
, zval
*zv TSRMLS_DC
)
50 php_trim(str
, len
, NULL
, 0, zv
, 3 TSRMLS_CC
);
53 if (Z_STRVAL_P(zv
)[0] == '"' && Z_STRVAL_P(zv
)[Z_STRLEN_P(zv
) - 1] == '"') {
54 size_t deq_len
= Z_STRLEN_P(zv
) - 2;
55 char *deq
= estrndup(Z_STRVAL_P(zv
) + 1, deq_len
);
58 ZVAL_STRINGL(zv
, deq
, deq_len
, 0);
62 php_stripslashes(Z_STRVAL_P(zv
), &Z_STRLEN_P(zv
) TSRMLS_CC
);
65 static void push_param(HashTable
*params
, php_http_params_state_t
*state
, const php_http_params_opts_t
*opts TSRMLS_DC
)
68 if (0 < (state
->val
.len
= state
->input
.str
- state
->val
.str
)) {
69 sanitize_string(state
->val
.str
, state
->val
.len
, *(state
->current
.val
) TSRMLS_CC
);
71 } else if (state
->arg
.str
) {
72 if (0 < (state
->arg
.len
= state
->input
.str
- state
->arg
.str
)) {
76 sanitize_string(state
->arg
.str
, state
->arg
.len
, &key TSRMLS_CC
);
80 zend_symtable_update(Z_ARRVAL_PP(state
->current
.args
), Z_STRVAL(key
), Z_STRLEN(key
) + 1, (void *) &val
, sizeof(zval
*), (void *) &state
->current
.val
);
84 } else if (state
->param
.str
) {
85 if (0 < (state
->param
.len
= state
->input
.str
- state
->param
.str
)) {
86 zval
*prm
, *arg
, *val
, key
;
89 sanitize_string(state
->param
.str
, state
->param
.len
, &key TSRMLS_CC
);
95 zend_hash_update(Z_ARRVAL_P(prm
), "value", sizeof("value"), (void *) &val
, sizeof(zval
*), (void *) &state
->current
.val
);
99 zend_hash_update(Z_ARRVAL_P(prm
), "arguments", sizeof("arguments"), (void *) &arg
, sizeof(zval
*), (void *) &state
->current
.args
);
101 zend_symtable_update(params
, Z_STRVAL(key
), Z_STRLEN(key
) + 1, (void *) &prm
, sizeof(zval
*), (void *) &state
->current
.param
);
108 static inline zend_bool
check_str(const char *chk_str
, size_t chk_len
, const char *sep_str
, size_t sep_len
) {
109 return 0 < sep_len
&& chk_len
>= sep_len
&& !memcmp(chk_str
, sep_str
, sep_len
);
112 static size_t check_sep(php_http_params_state_t
*state
, php_http_params_token_t
**separators
)
114 php_http_params_token_t
**sep
= separators
;
116 if (sep
) while (*sep
) {
117 if (check_str(state
->input
.str
, state
->input
.len
, (*sep
)->str
, (*sep
)->len
)) {
125 PHP_HTTP_API HashTable
*php_http_params_parse(HashTable
*params
, const php_http_params_opts_t
*opts TSRMLS_DC
)
127 php_http_params_state_t state
= {{NULL
,0}, {NULL
,0}, {NULL
,0}, {NULL
,0}, {NULL
,NULL
,NULL
}};
129 state
.input
.str
= opts
->input
.str
;
130 state
.input
.len
= opts
->input
.len
;
133 ALLOC_HASHTABLE(params
);
134 ZEND_INIT_SYMTABLE(params
);
137 while (state
.input
.len
) {
138 if (!state
.param
.str
) {
140 state
.param
.str
= state
.input
.str
;
143 /* are we at a param separator? */
144 if (0 < (sep_len
= check_sep(&state
, opts
->param
))) {
145 push_param(params
, &state
, opts TSRMLS_CC
);
147 /* start off with a new param */
148 state
.param
.str
= state
.input
.str
+ sep_len
;
150 state
.arg
.str
= NULL
;
152 state
.val
.str
= NULL
;
155 /* are we at an arg separator? */
156 if (0 < (sep_len
= check_sep(&state
, opts
->arg
))) {
157 push_param(params
, &state
, opts TSRMLS_CC
);
159 /* continue with a new arg */
160 state
.arg
.str
= state
.input
.str
+ sep_len
;
162 state
.val
.str
= NULL
;
165 /* are we at a val separator? */
166 if (0 < (sep_len
= check_sep(&state
, opts
->val
))) {
167 /* only handle separator if we're not already reading in a val */
168 if (!state
.val
.str
) {
169 push_param(params
, &state
, opts TSRMLS_CC
);
171 state
.val
.str
= state
.input
.str
+ sep_len
;
181 push_param(params
, &state
, opts TSRMLS_CC
);
186 PHP_HTTP_API php_http_buffer_t
*php_http_params_to_string(php_http_buffer_t
*buf
, HashTable
*params
, const char *pss
, size_t psl
, const char *ass
, size_t asl
, const char *vss
, size_t vsl TSRMLS_DC
)
189 HashPosition pos1
, pos2
;
190 php_http_array_hashkey_t key1
= php_http_array_hashkey_init(0), key2
= php_http_array_hashkey_init(0);
193 buf
= php_http_buffer_init(NULL
);
196 FOREACH_HASH_KEYVAL(pos1
, params
, key1
, zparam
) {
198 if (PHP_HTTP_BUFFER_LEN(buf
)) {
199 php_http_buffer_append(buf
, pss
, psl
);
203 if (key1
.type
== HASH_KEY_IS_STRING
) {
204 php_http_buffer_append(buf
, key1
.str
, key1
.len
- 1);
206 php_http_buffer_appendf(buf
, "%lu", key1
.num
);
209 if (Z_TYPE_PP(zparam
) != IS_ARRAY
) {
210 zval
*tmp
= php_http_ztyp(IS_STRING
, *zparam
);
212 php_http_buffer_append(buf
, vss
, vsl
);
213 php_http_buffer_append(buf
, Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
));
216 zval
**zvalue
, **zargs
, **zarg
;
219 if (SUCCESS
== zend_hash_find(Z_ARRVAL_PP(zparam
), ZEND_STRS("value"), (void *) &zvalue
)) {
220 if (Z_TYPE_PP(zvalue
) != IS_BOOL
) {
221 zval
*tmp
= php_http_ztyp(IS_STRING
, *zvalue
);
223 php_http_buffer_append(buf
, vss
, vsl
);
224 php_http_buffer_append(buf
, Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
));
226 } else if (!Z_BVAL_PP(zvalue
)) {
227 php_http_buffer_append(buf
, vss
, vsl
);
228 php_http_buffer_appends(buf
, "0");
232 if (SUCCESS
!= zend_hash_find(Z_ARRVAL_PP(zparam
), ZEND_STRS("arguments"), (void *) &zargs
)) {
236 if (Z_TYPE_PP(zargs
) == IS_ARRAY
) {
237 FOREACH_KEYVAL(pos2
, *zargs
, key2
, zarg
) {
238 /* skip "value" if zargs == zparam */
239 if (zargs
== zparam
&& key2
.type
== HASH_KEY_IS_STRING
&& !strcmp(key2
.str
, "value")) {
244 if (PHP_HTTP_BUFFER_LEN(buf
)) {
245 php_http_buffer_append(buf
, ass
, asl
);
249 if (key2
.type
== HASH_KEY_IS_STRING
) {
250 php_http_buffer_append(buf
, key2
.str
, key2
.len
- 1);
252 php_http_buffer_appendf(buf
, "%lu", key2
.num
);
255 if (Z_TYPE_PP(zarg
) != IS_BOOL
) {
256 zval
*tmp
= php_http_ztyp(IS_STRING
, *zarg
);
259 Z_STRVAL_P(tmp
) = php_addslashes(Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
), &escaped_len
, 1 TSRMLS_CC
);
260 php_http_buffer_append(buf
, vss
, vsl
);
261 if (escaped_len
!= Z_STRLEN_P(tmp
)) {
262 php_http_buffer_appends(buf
, "\"");
263 php_http_buffer_append(buf
, Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
) = escaped_len
);
264 php_http_buffer_appends(buf
, "\"");
266 php_http_buffer_append(buf
, Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
));
269 } else if (!Z_BVAL_PP(zarg
)) {
270 php_http_buffer_append(buf
, vss
, vsl
);
271 php_http_buffer_appends(buf
, "0");
278 php_http_buffer_shrink(buf
);
279 php_http_buffer_fix(buf
);
284 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpParams, method, 0, req_args)
285 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpParams, method, 0)
286 #define PHP_HTTP_PARAMS_ME(method, visibility) PHP_ME(HttpParams, method, PHP_HTTP_ARGS(HttpParams, method), visibility)
287 #define PHP_HTTP_PARAMS_GME(method, visibility) PHP_ME(HttpParams, method, PHP_HTTP_ARGS(HttpParams, __getter), visibility)
289 PHP_HTTP_BEGIN_ARGS(__construct
, 0)
290 PHP_HTTP_ARG_VAL(params
, 0)
291 PHP_HTTP_ARG_VAL(param_sep
, 0)
292 PHP_HTTP_ARG_VAL(arg_sep
, 0)
293 PHP_HTTP_ARG_VAL(val_sep
, 0)
296 PHP_HTTP_EMPTY_ARGS(toArray
);
297 PHP_HTTP_EMPTY_ARGS(toString
);
299 PHP_HTTP_BEGIN_ARGS(offsetExists
, 1)
300 PHP_HTTP_ARG_VAL(name
, 0)
303 PHP_HTTP_BEGIN_ARGS(offsetUnset
, 1)
304 PHP_HTTP_ARG_VAL(name
, 0)
307 PHP_HTTP_BEGIN_ARGS(offsetGet
, 1)
308 PHP_HTTP_ARG_VAL(name
, 0)
311 PHP_HTTP_BEGIN_ARGS(offsetSet
, 2)
312 PHP_HTTP_ARG_VAL(name
, 0)
313 PHP_HTTP_ARG_VAL(value
, 0)
316 static zend_class_entry
*php_http_params_class_entry
;
318 zend_class_entry
*php_http_params_get_class_entry(void)
320 return php_http_params_class_entry
;
323 static zend_function_entry php_http_params_method_entry
[] = {
324 PHP_HTTP_PARAMS_ME(__construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
|ZEND_ACC_FINAL
)
326 PHP_HTTP_PARAMS_ME(toArray
, ZEND_ACC_PUBLIC
)
327 PHP_HTTP_PARAMS_ME(toString
, ZEND_ACC_PUBLIC
)
328 ZEND_MALIAS(HttpParams
, __toString
, toString
, PHP_HTTP_ARGS(HttpParams
, toString
), ZEND_ACC_PUBLIC
)
330 PHP_HTTP_PARAMS_ME(offsetExists
, ZEND_ACC_PUBLIC
)
331 PHP_HTTP_PARAMS_ME(offsetUnset
, ZEND_ACC_PUBLIC
)
332 PHP_HTTP_PARAMS_ME(offsetSet
, ZEND_ACC_PUBLIC
)
333 PHP_HTTP_PARAMS_ME(offsetGet
, ZEND_ACC_PUBLIC
)
338 PHP_MINIT_FUNCTION(http_params
)
340 PHP_HTTP_REGISTER_CLASS(http
, Params
, http_params
, php_http_object_get_class_entry(), 0);
342 zend_class_implements(php_http_params_class_entry TSRMLS_CC
, 1, zend_ce_arrayaccess
);
344 zend_declare_class_constant_stringl(php_http_params_class_entry
, ZEND_STRL("DEF_PARAM_SEP"), ZEND_STRL(",") TSRMLS_CC
);
345 zend_declare_class_constant_stringl(php_http_params_class_entry
, ZEND_STRL("DEF_ARG_SEP"), ZEND_STRL(";") TSRMLS_CC
);
346 zend_declare_class_constant_stringl(php_http_params_class_entry
, ZEND_STRL("DEF_VAL_SEP"), ZEND_STRL("=") TSRMLS_CC
);
347 zend_declare_class_constant_stringl(php_http_params_class_entry
, ZEND_STRL("COOKIE_PARAM_SEP"), ZEND_STRL("") TSRMLS_CC
);
349 zend_declare_property_null(php_http_params_class_entry
, ZEND_STRL("params"), ZEND_ACC_PUBLIC TSRMLS_CC
);
350 zend_declare_property_stringl(php_http_params_class_entry
, ZEND_STRL("param_sep"), ZEND_STRL(","), ZEND_ACC_PUBLIC TSRMLS_CC
);
351 zend_declare_property_stringl(php_http_params_class_entry
, ZEND_STRL("arg_sep"), ZEND_STRL(";"), ZEND_ACC_PUBLIC TSRMLS_CC
);
352 zend_declare_property_stringl(php_http_params_class_entry
, ZEND_STRL("val_sep"), ZEND_STRL("="), ZEND_ACC_PUBLIC TSRMLS_CC
);
357 static php_http_params_token_t
**parse_sep(zval
*zv TSRMLS_DC
)
361 php_http_params_token_t
**ret
, **tmp
;
367 zv
= php_http_ztyp(IS_ARRAY
, zv
);
368 ret
= ecalloc(zend_hash_num_elements(Z_ARRVAL_P(zv
)) + 1, sizeof(*ret
));
371 FOREACH_VAL(pos
, zv
, sep
) {
372 zval
*zt
= php_http_ztyp(IS_STRING
, *sep
);
374 if (Z_STRLEN_P(zt
)) {
375 *tmp
= emalloc(sizeof(**tmp
));
376 (*tmp
)->str
= estrndup(Z_STRVAL_P(zt
), (*tmp
)->len
= Z_STRLEN_P(zt
));
387 static void free_sep(php_http_params_token_t
**separator
) {
388 php_http_params_token_t
**sep
= separator
;
391 STR_FREE((*sep
)->str
);
399 PHP_METHOD(HttpParams
, __construct
)
401 with_error_handling(EH_THROW
, php_http_exception_get_class_entry()) {
402 zval
*zcopy
, *zparams
= NULL
, *param_sep
= NULL
, *arg_sep
= NULL
, *val_sep
= NULL
;
404 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|z!/z/z/z/", &zparams
, ¶m_sep
, &arg_sep
, &val_sep
)) {
405 switch (ZEND_NUM_ARGS()) {
407 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("val_sep"), val_sep TSRMLS_CC
);
410 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("arg_sep"), arg_sep TSRMLS_CC
);
413 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("param_sep"), param_sep TSRMLS_CC
);
418 switch (Z_TYPE_P(zparams
)) {
421 zcopy
= php_http_zsep(1, IS_ARRAY
, zparams
);
422 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), zcopy TSRMLS_CC
);
423 zval_ptr_dtor(&zcopy
);
426 zcopy
= php_http_ztyp(IS_STRING
, zparams
);
427 if (Z_STRLEN_P(zcopy
)) {
428 php_http_params_opts_t opts
= {
430 .str
= Z_STRVAL_P(zcopy
),
431 .len
= Z_STRLEN_P(zcopy
)
433 .param
= parse_sep(zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC
) TSRMLS_CC
),
434 .arg
= parse_sep(zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC
) TSRMLS_CC
),
435 .val
= parse_sep(zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC
) TSRMLS_CC
)
438 MAKE_STD_ZVAL(zparams
);
440 php_http_params_parse(Z_ARRVAL_P(zparams
), &opts TSRMLS_CC
);
441 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC
);
442 zval_ptr_dtor(&zparams
);
444 free_sep(opts
.param
);
448 zval_ptr_dtor(&zcopy
);
452 MAKE_STD_ZVAL(zparams
);
454 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC
);
455 zval_ptr_dtor(&zparams
);
458 } end_error_handling();
461 PHP_METHOD(HttpParams
, toArray
)
463 if (SUCCESS
!= zend_parse_parameters_none()) {
466 RETURN_PROP(php_http_params_class_entry
, "params");
469 PHP_METHOD(HttpParams
, toString
)
471 zval
*zparams
, *zpsep
, *zasep
, *zvsep
;
472 php_http_buffer_t buf
;
474 zparams
= php_http_ztyp(IS_ARRAY
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC
));
475 zpsep
= php_http_ztyp(IS_STRING
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC
));
476 zasep
= php_http_ztyp(IS_STRING
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC
));
477 zvsep
= php_http_ztyp(IS_STRING
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC
));
479 php_http_buffer_init(&buf
);
480 php_http_params_to_string(&buf
, Z_ARRVAL_P(zparams
), Z_STRVAL_P(zpsep
), Z_STRLEN_P(zpsep
), Z_STRVAL_P(zasep
), Z_STRLEN_P(zasep
), Z_STRVAL_P(zvsep
), Z_STRLEN_P(zvsep
) TSRMLS_CC
);
482 zval_ptr_dtor(&zparams
);
483 zval_ptr_dtor(&zpsep
);
484 zval_ptr_dtor(&zasep
);
485 zval_ptr_dtor(&zvsep
);
487 RETVAL_PHP_HTTP_BUFFER_VAL(&buf
);
490 PHP_METHOD(HttpParams
, offsetExists
)
495 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &name_str
, &name_len
)) {
496 zval
**zparam
, *zparams
= php_http_ztyp(IS_ARRAY
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC
));
498 if (SUCCESS
== zend_symtable_find(Z_ARRVAL_P(zparams
), name_str
, name_len
+ 1, (void *) &zparam
)) {
499 RETVAL_BOOL(Z_TYPE_PP(zparam
) != IS_NULL
);
503 zval_ptr_dtor(&zparams
);
507 PHP_METHOD(HttpParams
, offsetGet
)
512 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &name_str
, &name_len
)) {
513 zval
**zparam
, *zparams
= php_http_ztyp(IS_ARRAY
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC
));
515 if (SUCCESS
== zend_symtable_find(Z_ARRVAL_P(zparams
), name_str
, name_len
+ 1, (void *) &zparam
)) {
516 RETVAL_ZVAL(*zparam
, 1, 0);
519 zval_ptr_dtor(&zparams
);
524 PHP_METHOD(HttpParams
, offsetUnset
)
529 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &name_str
, &name_len
)) {
530 zval
*zparams
= php_http_zsep(1, IS_ARRAY
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC
));
532 zend_symtable_del(Z_ARRVAL_P(zparams
), name_str
, name_len
+ 1);
533 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC
);
535 zval_ptr_dtor(&zparams
);
539 PHP_METHOD(HttpParams
, offsetSet
)
545 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "sz", &name_str
, &name_len
, &nvalue
)) {
546 zval
**zparam
, *zparams
= php_http_zsep(1, IS_ARRAY
, zend_read_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC
));
549 if (Z_TYPE_P(nvalue
) == IS_ARRAY
) {
552 if (SUCCESS
== zend_symtable_find(Z_ARRVAL_P(zparams
), name_str
, name_len
+ 1, (void *) &zparam
)) {
553 new_zparam
= php_http_zsep(1, IS_ARRAY
, *zparam
);
554 array_join(Z_ARRVAL_P(nvalue
), Z_ARRVAL_P(new_zparam
), 0, 0);
557 Z_ADDREF_P(new_zparam
);
559 add_assoc_zval_ex(zparams
, name_str
, name_len
+ 1, new_zparam
);
563 if (SUCCESS
== zend_symtable_find(Z_ARRVAL_P(zparams
), name_str
, name_len
+ 1, (void *) &zparam
)) {
564 tmp
= php_http_zsep(1, IS_ARRAY
, *zparam
);
571 add_assoc_zval_ex(tmp
, ZEND_STRS("value"), nvalue
);
572 add_assoc_zval_ex(zparams
, name_str
, name_len
+ 1, tmp
);
575 zval
*tmp
= php_http_ztyp(IS_STRING
, nvalue
), *arr
;
579 add_assoc_bool_ex(arr
, ZEND_STRS("value"), 1);
580 add_assoc_zval_ex(zparams
, Z_STRVAL_P(tmp
), Z_STRLEN_P(tmp
) + 1, arr
);
584 zend_update_property(php_http_params_class_entry
, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC
);
585 zval_ptr_dtor(&zparams
);
594 * vim600: noet sw=4 ts=4 fdm=marker
595 * vim<600: noet sw=4 ts=4