fix the fix
[m6w6/ext-raphf] / php_raphf.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: raphf |
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) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include "php.h"
18 #include "php_ini.h"
19 #include "ext/standard/info.h"
20 #include "php_raphf.h"
21
22 #ifndef PHP_RAPHF_TEST
23 # define PHP_RAPHF_TEST 0
24 #endif
25
26 struct php_persistent_handle_globals {
27 ulong limit;
28 HashTable hash;
29 };
30
31 ZEND_BEGIN_MODULE_GLOBALS(raphf)
32 struct php_persistent_handle_globals persistent_handle;
33 ZEND_END_MODULE_GLOBALS(raphf)
34
35 #ifdef ZTS
36 # define PHP_RAPHF_G ((zend_raphf_globals *) \
37 (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(raphf_globals_id)])
38 #else
39 # define PHP_RAPHF_G (&raphf_globals)
40 #endif
41
42 ZEND_DECLARE_MODULE_GLOBALS(raphf)
43
44 #ifndef PHP_RAPHF_DEBUG_PHANDLES
45 # define PHP_RAPHF_DEBUG_PHANDLES 0
46 #endif
47 #if PHP_RAPHF_DEBUG_PHANDLES
48 # undef inline
49 # define inline
50 #endif
51
52 php_resource_factory_t *php_resource_factory_init(php_resource_factory_t *f,
53 php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *data))
54 {
55 if (!f) {
56 f = emalloc(sizeof(*f));
57 }
58 memset(f, 0, sizeof(*f));
59
60 memcpy(&f->fops, fops, sizeof(*fops));
61
62 f->data = data;
63 f->dtor = dtor;
64
65 f->refcount = 1;
66
67 return f;
68 }
69
70 unsigned php_resource_factory_addref(php_resource_factory_t *rf)
71 {
72 return ++rf->refcount;
73 }
74
75 void php_resource_factory_dtor(php_resource_factory_t *f)
76 {
77 if (!--f->refcount) {
78 if (f->dtor) {
79 f->dtor(f->data);
80 }
81 }
82 }
83
84 void php_resource_factory_free(php_resource_factory_t **f)
85 {
86 if (*f) {
87 php_resource_factory_dtor(*f);
88 if (!(*f)->refcount) {
89 efree(*f);
90 *f = NULL;
91 }
92 }
93 }
94
95 void *php_resource_factory_handle_ctor(php_resource_factory_t *f, void *init_arg)
96 {
97 if (f->fops.ctor) {
98 return f->fops.ctor(f->data, init_arg);
99 }
100 return NULL;
101 }
102
103 void *php_resource_factory_handle_copy(php_resource_factory_t *f, void *handle)
104 {
105 if (f->fops.copy) {
106 return f->fops.copy(f->data, handle);
107 }
108 return NULL;
109 }
110
111 void php_resource_factory_handle_dtor(php_resource_factory_t *f, void *handle)
112 {
113 if (f->fops.dtor) {
114 f->fops.dtor(f->data, handle);
115 }
116 }
117
118 static inline php_persistent_handle_list_t *php_persistent_handle_list_init(
119 php_persistent_handle_list_t *list)
120 {
121 if (!list) {
122 list = pemalloc(sizeof(*list), 1);
123 }
124 list->used = 0;
125 zend_hash_init(&list->free, 0, NULL, NULL, 1);
126
127 return list;
128 }
129
130 static int php_persistent_handle_apply_stat(zval *p, int argc, va_list argv,
131 zend_hash_key *key)
132 {
133 php_persistent_handle_list_t *list = Z_PTR_P(p);
134 zval zsubentry, *zentry = va_arg(argv, zval *);
135
136 array_init(&zsubentry);
137 add_assoc_long_ex(&zsubentry, ZEND_STRL("used"), list->used);
138 add_assoc_long_ex(&zsubentry, ZEND_STRL("free"),
139 zend_hash_num_elements(&list->free));
140 if (key->key) {
141 add_assoc_zval_ex(zentry, key->key->val, key->key->len, &zsubentry);
142 } else {
143 add_index_zval(zentry, key->h, &zsubentry);
144 }
145 return ZEND_HASH_APPLY_KEEP;
146 }
147
148 static int php_persistent_handle_apply_statall(zval *p, int argc, va_list argv,
149 zend_hash_key *key)
150 {
151 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
152 HashTable *ht = va_arg(argv, HashTable *);
153 zval zentry;
154
155 array_init(&zentry);
156
157 zend_hash_apply_with_arguments(&provider->list.free,
158 php_persistent_handle_apply_stat, 1, &zentry);
159
160 if (key->key) {
161 zend_hash_update(ht, key->key, &zentry);
162 } else {
163 zend_hash_index_update(ht, key->h, &zentry);
164 }
165
166 return ZEND_HASH_APPLY_KEEP;
167 }
168
169 static int php_persistent_handle_apply_cleanup_ex(zval *p, void *arg)
170 {
171 php_resource_factory_t *rf = arg;
172 void *handle = Z_PTR_P(p);
173
174 #if PHP_RAPHF_DEBUG_PHANDLES
175 fprintf(stderr, "DESTROY: %p\n", handle);
176 #endif
177 php_resource_factory_handle_dtor(rf, handle);
178 return ZEND_HASH_APPLY_REMOVE;
179 }
180
181 static int php_persistent_handle_apply_cleanup(zval *p, void *arg)
182 {
183 php_resource_factory_t *rf = arg;
184 php_persistent_handle_list_t *list = Z_PTR_P(p);
185
186 zend_hash_apply_with_argument(&list->free,
187 php_persistent_handle_apply_cleanup_ex, rf);
188 if (list->used) {
189 return ZEND_HASH_APPLY_KEEP;
190 }
191 zend_hash_destroy(&list->free);
192 #if PHP_RAPHF_DEBUG_PHANDLES
193 fprintf(stderr, "LSTFREE: %p\n", list);
194 #endif
195 pefree(list, 1);
196 return ZEND_HASH_APPLY_REMOVE;
197 }
198
199 static inline void php_persistent_handle_list_dtor(
200 php_persistent_handle_list_t *list,
201 php_persistent_handle_provider_t *provider)
202 {
203 #if PHP_RAPHF_DEBUG_PHANDLES
204 fprintf(stderr, "LSTDTOR: %p\n", list);
205 #endif
206 zend_hash_apply_with_argument(&list->free,
207 php_persistent_handle_apply_cleanup_ex, &provider->rf);
208 zend_hash_destroy(&list->free);
209 }
210
211 static inline void php_persistent_handle_list_free(
212 php_persistent_handle_list_t **list,
213 php_persistent_handle_provider_t *provider)
214 {
215 php_persistent_handle_list_dtor(*list, provider);
216 #if PHP_RAPHF_DEBUG_PHANDLES
217 fprintf(stderr, "LSTFREE: %p\n", *list);
218 #endif
219 pefree(*list, 1);
220 *list = NULL;
221 }
222
223 static int php_persistent_handle_list_apply_dtor(zval *p, void *provider)
224 {
225 php_persistent_handle_list_t *list = Z_PTR_P(p);
226
227 php_persistent_handle_list_free(&list, provider );
228 ZVAL_PTR(p, NULL);
229 return ZEND_HASH_APPLY_REMOVE;
230 }
231
232 static inline php_persistent_handle_list_t *php_persistent_handle_list_find(
233 php_persistent_handle_provider_t *provider, zend_string *ident)
234 {
235 php_persistent_handle_list_t *list;
236 zval *zlist = zend_symtable_find(&provider->list.free, ident);
237
238 if (zlist && (list = Z_PTR_P(zlist))) {
239 #if PHP_RAPHF_DEBUG_PHANDLES
240 fprintf(stderr, "LSTFIND: %p\n", list);
241 #endif
242 return list;
243 }
244
245 if ((list = php_persistent_handle_list_init(NULL))) {
246 zval p, *rv;
247 zend_string *id;
248
249 ZVAL_PTR(&p, list);
250 if ((GC_FLAGS(ident) & IS_STR_PERSISTENT)) {
251 id = ident;
252 } else {
253 id = zend_string_dup(ident, 1);
254 }
255 rv = zend_symtable_update(&provider->list.free, id, &p);
256 if (id != ident) {
257 zend_string_release(id);
258 }
259
260 if (rv) {
261 #if PHP_RAPHF_DEBUG_PHANDLES
262 fprintf(stderr, "LSTFIND: %p (new)\n", list);
263 #endif
264 return list;
265 }
266 php_persistent_handle_list_free(&list, provider);
267 }
268
269 return NULL;
270 }
271
272 static int php_persistent_handle_apply_cleanup_all(zval *p, int argc,
273 va_list argv, zend_hash_key *key)
274 {
275 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
276 zend_string *ident = va_arg(argv, zend_string *);
277 php_persistent_handle_list_t *list;
278
279 if (ident && ident->len) {
280 if ((list = php_persistent_handle_list_find(provider, ident))) {
281 zend_hash_apply_with_argument(&list->free,
282 php_persistent_handle_apply_cleanup_ex,
283 &provider->rf);
284 }
285 } else {
286 zend_hash_apply_with_argument(&provider->list.free,
287 php_persistent_handle_apply_cleanup, &provider->rf);
288 }
289
290 return ZEND_HASH_APPLY_KEEP;
291 }
292
293 static void php_persistent_handle_hash_dtor(zval *p)
294 {
295 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
296
297 zend_hash_apply_with_argument(&provider->list.free,
298 php_persistent_handle_list_apply_dtor, provider);
299 zend_hash_destroy(&provider->list.free);
300 php_resource_factory_dtor(&provider->rf);
301 pefree(provider, 1);
302 }
303
304 ZEND_RESULT_CODE php_persistent_handle_provide(zend_string *name,
305 php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *))
306 {
307 php_persistent_handle_provider_t *provider = pemalloc(sizeof(*provider), 1);
308
309 if (php_persistent_handle_list_init(&provider->list)) {
310 if (php_resource_factory_init(&provider->rf, fops, data, dtor)) {
311 zval p, *rv;
312 zend_string *ns;
313
314 #if PHP_RAPHF_DEBUG_PHANDLES
315 fprintf(stderr, "PROVIDE: %p %s\n", PHP_RAPHF_G, name_str);
316 #endif
317
318 ZVAL_PTR(&p, provider);
319 if ((GC_FLAGS(name) & IS_STR_PERSISTENT)) {
320 ns = name;
321 } else {
322 ns = zend_string_dup(name, 1);
323 }
324 rv = zend_symtable_update(&PHP_RAPHF_G->persistent_handle.hash, ns, &p);
325 if (ns != name) {
326 zend_string_release(ns);
327 }
328 if (rv) {
329 return SUCCESS;
330 }
331 php_resource_factory_dtor(&provider->rf);
332 }
333 }
334
335 return FAILURE;
336 }
337
338
339 php_persistent_handle_factory_t *php_persistent_handle_concede(
340 php_persistent_handle_factory_t *a,
341 zend_string *name, zend_string *ident,
342 php_persistent_handle_wakeup_t wakeup,
343 php_persistent_handle_retire_t retire)
344 {
345 zval *zprovider = zend_symtable_find(&PHP_RAPHF_G->persistent_handle.hash, name);
346
347 if (zprovider) {
348 zend_bool free_a = 0;
349
350 if ((free_a = !a)) {
351 a = emalloc(sizeof(*a));
352 }
353 memset(a, 0, sizeof(*a));
354
355 a->provider = Z_PTR_P(zprovider);
356 a->ident = zend_string_copy(ident);
357 a->wakeup = wakeup;
358 a->retire = retire;
359 a->free_on_abandon = free_a;
360 } else {
361 a = NULL;
362 }
363
364 #if PHP_RAPHF_DEBUG_PHANDLES
365 fprintf(stderr, "CONCEDE: %p %p (%s) (%s)\n", PHP_RAPHF_G,
366 a ? a->provider : NULL, name->val, ident->val);
367 #endif
368
369 return a;
370 }
371
372 void php_persistent_handle_abandon(php_persistent_handle_factory_t *a)
373 {
374 zend_bool f = a->free_on_abandon;
375
376 #if PHP_RAPHF_DEBUG_PHANDLES
377 fprintf(stderr, "ABANDON: %p\n", a->provider);
378 #endif
379
380 zend_string_release(a->ident);
381 memset(a, 0, sizeof(*a));
382 if (f) {
383 efree(a);
384 }
385 }
386
387 void *php_persistent_handle_acquire(php_persistent_handle_factory_t *a, void *init_arg)
388 {
389 int key;
390 zval *p;
391 zend_ulong index;
392 void *handle = NULL;
393 php_persistent_handle_list_t *list;
394
395 list = php_persistent_handle_list_find(a->provider, a->ident);
396 if (list) {
397 zend_hash_internal_pointer_end(&list->free);
398 key = zend_hash_get_current_key(&list->free, NULL, &index);
399 p = zend_hash_get_current_data(&list->free);
400 if (p && HASH_KEY_NON_EXISTENT != key) {
401 handle = Z_PTR_P(p);
402 if (a->wakeup) {
403 a->wakeup(a, &handle);
404 }
405 zend_hash_index_del(&list->free, index);
406 } else {
407 handle = php_resource_factory_handle_ctor(&a->provider->rf, init_arg);
408 }
409 #if PHP_RAPHF_DEBUG_PHANDLES
410 fprintf(stderr, "CREATED: %p\n", handle);
411 #endif
412 if (handle) {
413 ++a->provider->list.used;
414 ++list->used;
415 }
416 }
417
418 return handle;
419 }
420
421 void *php_persistent_handle_accrete(php_persistent_handle_factory_t *a, void *handle)
422 {
423 void *new_handle = NULL;
424 php_persistent_handle_list_t *list;
425
426 new_handle = php_resource_factory_handle_copy(&a->provider->rf, handle);
427 if (handle) {
428 list = php_persistent_handle_list_find(a->provider, a->ident);
429 if (list) {
430 ++list->used;
431 }
432 ++a->provider->list.used;
433 }
434
435 return new_handle;
436 }
437
438 void php_persistent_handle_release(php_persistent_handle_factory_t *a, void *handle)
439 {
440 php_persistent_handle_list_t *list;
441
442 list = php_persistent_handle_list_find(a->provider, a->ident);
443 if (list) {
444 if (a->provider->list.used >= PHP_RAPHF_G->persistent_handle.limit) {
445 #if PHP_RAPHF_DEBUG_PHANDLES
446 fprintf(stderr, "DESTROY: %p\n", handle);
447 #endif
448 php_resource_factory_handle_dtor(&a->provider->rf, handle);
449 } else {
450 if (a->retire) {
451 a->retire(a, &handle);
452 }
453 zend_hash_next_index_insert_ptr(&list->free, handle);
454 }
455
456 --a->provider->list.used;
457 --list->used;
458 }
459 }
460
461 void php_persistent_handle_cleanup(zend_string *name, zend_string *ident)
462 {
463 php_persistent_handle_provider_t *provider;
464 php_persistent_handle_list_t *list;
465
466 if (name) {
467 zval *zprovider = zend_symtable_find(&PHP_RAPHF_G->persistent_handle.hash,
468 name);
469
470 if (zprovider && (provider = Z_PTR_P(zprovider))) {
471 if (ident) {
472 list = php_persistent_handle_list_find(provider, ident);
473 if (list) {
474 zend_hash_apply_with_argument(&list->free,
475 php_persistent_handle_apply_cleanup_ex,
476 &provider->rf);
477 }
478 } else {
479 zend_hash_apply_with_argument(&provider->list.free,
480 php_persistent_handle_apply_cleanup,
481 &provider->rf);
482 }
483 }
484 } else {
485 zend_hash_apply_with_arguments(
486 &PHP_RAPHF_G->persistent_handle.hash,
487 php_persistent_handle_apply_cleanup_all, 1, ident);
488 }
489 }
490
491 HashTable *php_persistent_handle_statall(HashTable *ht)
492 {
493 if (zend_hash_num_elements(&PHP_RAPHF_G->persistent_handle.hash)) {
494 if (!ht) {
495 ALLOC_HASHTABLE(ht);
496 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
497 }
498 zend_hash_apply_with_arguments(
499 &PHP_RAPHF_G->persistent_handle.hash,
500 php_persistent_handle_apply_statall, 1, ht);
501 } else if (ht) {
502 ht = NULL;
503 }
504
505 return ht;
506 }
507
508 static php_resource_factory_ops_t php_persistent_handle_resource_factory_ops = {
509 (php_resource_factory_handle_ctor_t) php_persistent_handle_acquire,
510 (php_resource_factory_handle_copy_t) php_persistent_handle_accrete,
511 (php_resource_factory_handle_dtor_t) php_persistent_handle_release
512 };
513
514 php_resource_factory_ops_t *php_persistent_handle_get_resource_factory_ops(void)
515 {
516 return &php_persistent_handle_resource_factory_ops;
517 }
518
519 ZEND_BEGIN_ARG_INFO_EX(ai_raphf_stat_persistent_handles, 0, 0, 0)
520 ZEND_END_ARG_INFO();
521 static PHP_FUNCTION(raphf_stat_persistent_handles)
522 {
523 if (SUCCESS == zend_parse_parameters_none()) {
524 object_init(return_value);
525 if (php_persistent_handle_statall(HASH_OF(return_value))) {
526 return;
527 }
528 zval_dtor(return_value);
529 }
530 RETURN_FALSE;
531 }
532
533 ZEND_BEGIN_ARG_INFO_EX(ai_raphf_clean_persistent_handles, 0, 0, 0)
534 ZEND_ARG_INFO(0, name)
535 ZEND_ARG_INFO(0, ident)
536 ZEND_END_ARG_INFO();
537 static PHP_FUNCTION(raphf_clean_persistent_handles)
538 {
539 zend_string *name = NULL, *ident = NULL;
540
541 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|S!S!", &name, &ident)) {
542 php_persistent_handle_cleanup(name, ident);
543 }
544 }
545
546 #if PHP_RAPHF_TEST
547 # include "php_raphf_test.c"
548 #endif
549
550 static const zend_function_entry raphf_functions[] = {
551 ZEND_NS_FENTRY("raphf", stat_persistent_handles,
552 ZEND_FN(raphf_stat_persistent_handles),
553 ai_raphf_stat_persistent_handles, 0)
554 ZEND_NS_FENTRY("raphf", clean_persistent_handles,
555 ZEND_FN(raphf_clean_persistent_handles),
556 ai_raphf_clean_persistent_handles, 0)
557 #if PHP_RAPHF_TEST
558 ZEND_NS_FENTRY("raphf", provide, ZEND_FN(raphf_provide), NULL, 0)
559 ZEND_NS_FENTRY("raphf", conceal, ZEND_FN(raphf_conceal), NULL, 0)
560 ZEND_NS_FENTRY("raphf", concede, ZEND_FN(raphf_concede), NULL, 0)
561 ZEND_NS_FENTRY("raphf", dispute, ZEND_FN(raphf_dispute), NULL, 0)
562 ZEND_NS_FENTRY("raphf", handle_ctor, ZEND_FN(raphf_handle_ctor), NULL, 0)
563 ZEND_NS_FENTRY("raphf", handle_copy, ZEND_FN(raphf_handle_copy), NULL, 0)
564 ZEND_NS_FENTRY("raphf", handle_dtor, ZEND_FN(raphf_handle_dtor), NULL, 0)
565 #endif
566 {0}
567 };
568
569 PHP_INI_BEGIN()
570 STD_PHP_INI_ENTRY("raphf.persistent_handle.limit", "-1", PHP_INI_SYSTEM,
571 OnUpdateLong, persistent_handle.limit, zend_raphf_globals,
572 raphf_globals)
573 PHP_INI_END()
574
575 static HashTable *php_persistent_handles_global_hash;
576
577 static PHP_GINIT_FUNCTION(raphf)
578 {
579 raphf_globals->persistent_handle.limit = -1;
580
581 zend_hash_init(&raphf_globals->persistent_handle.hash, 0, NULL,
582 php_persistent_handle_hash_dtor, 1);
583 if (php_persistent_handles_global_hash) {
584 zend_hash_copy(&raphf_globals->persistent_handle.hash,
585 php_persistent_handles_global_hash, NULL);
586 }
587 }
588
589 static PHP_GSHUTDOWN_FUNCTION(raphf)
590 {
591 zend_hash_destroy(&raphf_globals->persistent_handle.hash);
592 }
593
594 PHP_MINIT_FUNCTION(raphf)
595 {
596 php_persistent_handles_global_hash = &PHP_RAPHF_G->persistent_handle.hash;
597
598 #if PHP_RAPHF_TEST
599 PHP_MINIT(raphf_test)(INIT_FUNC_ARGS_PASSTHRU);
600 #endif
601
602 REGISTER_INI_ENTRIES();
603 return SUCCESS;
604 }
605
606 PHP_MSHUTDOWN_FUNCTION(raphf)
607 {
608 #if PHP_RAPHF_TEST
609 PHP_MSHUTDOWN(raphf_test)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
610 #endif
611
612 UNREGISTER_INI_ENTRIES();
613 php_persistent_handles_global_hash = NULL;
614 return SUCCESS;
615 }
616
617 static int php_persistent_handle_apply_info_ex(zval *p, int argc,
618 va_list argv, zend_hash_key *key)
619 {
620 php_persistent_handle_list_t *list = Z_PTR_P(p);
621 zend_hash_key *super_key = va_arg(argv, zend_hash_key *);
622 char used[21], free[21];
623
624 slprintf(used, sizeof(used), "%u", list->used);
625 slprintf(free, sizeof(free), "%d", zend_hash_num_elements(&list->free));
626
627 php_info_print_table_row(4, super_key->key->val, key->key->val, used, free);
628
629 return ZEND_HASH_APPLY_KEEP;
630 }
631
632 static int php_persistent_handle_apply_info(zval *p, int argc,
633 va_list argv, zend_hash_key *key)
634 {
635 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
636
637 zend_hash_apply_with_arguments(&provider->list.free,
638 php_persistent_handle_apply_info_ex, 1, key);
639
640 return ZEND_HASH_APPLY_KEEP;
641 }
642
643 PHP_MINFO_FUNCTION(raphf)
644 {
645 php_info_print_table_start();
646 php_info_print_table_header(2,
647 "Resource and persistent handle factory support", "enabled");
648 php_info_print_table_row(2, "Extension version", PHP_RAPHF_VERSION);
649 php_info_print_table_end();
650
651 php_info_print_table_start();
652 php_info_print_table_colspan_header(4, "Persistent handles in this "
653 #ifdef ZTS
654 "thread"
655 #else
656 "process"
657 #endif
658 );
659 php_info_print_table_header(4, "Provider", "Ident", "Used", "Free");
660 zend_hash_apply_with_arguments(
661 &PHP_RAPHF_G->persistent_handle.hash,
662 php_persistent_handle_apply_info, 0);
663 php_info_print_table_end();
664
665 DISPLAY_INI_ENTRIES();
666 }
667
668 zend_module_entry raphf_module_entry = {
669 STANDARD_MODULE_HEADER,
670 "raphf",
671 raphf_functions,
672 PHP_MINIT(raphf),
673 PHP_MSHUTDOWN(raphf),
674 NULL,
675 NULL,
676 PHP_MINFO(raphf),
677 PHP_RAPHF_VERSION,
678 ZEND_MODULE_GLOBALS(raphf),
679 PHP_GINIT(raphf),
680 PHP_GSHUTDOWN(raphf),
681 NULL,
682 STANDARD_MODULE_PROPERTIES_EX
683 };
684 /* }}} */
685
686 #ifdef COMPILE_DL_RAPHF
687 ZEND_GET_MODULE(raphf)
688 #endif
689
690 /*
691 * Local variables:
692 * tab-width: 4
693 * c-basic-offset: 4
694 * End:
695 * vim600: noet sw=4 ts=4 fdm=marker
696 * vim<600: noet sw=4 ts=4
697 */