bye bye TSRM*
[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, const char *ident_str,
234 size_t ident_len)
235 {
236 php_persistent_handle_list_t *list;
237
238 list = zend_symtable_str_find_ptr(&provider->list.free, ident_str, ident_len);
239
240 if (list) {
241 #if PHP_RAPHF_DEBUG_PHANDLES
242 fprintf(stderr, "LSTFIND: %p\n", list);
243 #endif
244 return list;
245 }
246
247 if ((list = php_persistent_handle_list_init(NULL))) {
248 zval p;
249
250 ZVAL_PTR(&p, list);
251 if (zend_symtable_str_update(&provider->list.free, ident_str, ident_len,
252 &p)) {
253 #if PHP_RAPHF_DEBUG_PHANDLES
254 fprintf(stderr, "LSTFIND: %p (new)\n", list);
255 #endif
256 return list;
257 }
258 php_persistent_handle_list_free(&list, provider);
259 }
260
261 return NULL;
262 }
263
264 static int php_persistent_handle_apply_cleanup_all(zval *p, int argc,
265 va_list argv, zend_hash_key *key)
266 {
267 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
268 const char *ident_str = va_arg(argv, const char *);
269 size_t ident_len = va_arg(argv, size_t);
270 php_persistent_handle_list_t *list;
271
272 if (ident_str && ident_len) {
273 if ((list = php_persistent_handle_list_find(provider, ident_str,
274 ident_len))) {
275 zend_hash_apply_with_argument(&list->free,
276 php_persistent_handle_apply_cleanup_ex,
277 &provider->rf);
278 }
279 } else {
280 zend_hash_apply_with_argument(&provider->list.free,
281 php_persistent_handle_apply_cleanup, &provider->rf);
282 }
283
284 return ZEND_HASH_APPLY_KEEP;
285 }
286
287 static void php_persistent_handle_hash_dtor(zval *p)
288 {
289 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
290
291 zend_hash_apply_with_argument(&provider->list.free,
292 php_persistent_handle_list_apply_dtor, provider);
293 zend_hash_destroy(&provider->list.free);
294 php_resource_factory_dtor(&provider->rf);
295 pefree(provider, 1);
296 }
297
298 ZEND_RESULT_CODE php_persistent_handle_provide(const char *name_str,
299 size_t name_len, php_resource_factory_ops_t *fops, void *data,
300 void (*dtor)(void *))
301 {
302 php_persistent_handle_provider_t *provider = pemalloc(sizeof(*provider), 1);
303
304 if (php_persistent_handle_list_init(&provider->list)) {
305 if (php_resource_factory_init(&provider->rf, fops, data, dtor)) {
306 zval p;
307
308 #if PHP_RAPHF_DEBUG_PHANDLES
309 fprintf(stderr, "PROVIDE: %p %s\n", PHP_RAPHF_G, name_str);
310 #endif
311
312 ZVAL_PTR(&p, provider);
313 if (zend_symtable_str_update(&PHP_RAPHF_G->persistent_handle.hash,
314 name_str, name_len, &p)) {
315 return SUCCESS;
316 }
317 php_resource_factory_dtor(&provider->rf);
318 }
319 }
320
321 return FAILURE;
322 }
323
324
325 php_persistent_handle_factory_t *php_persistent_handle_concede(
326 php_persistent_handle_factory_t *a, const char *name_str,
327 size_t name_len, const char *ident_str, size_t ident_len,
328 php_persistent_handle_wakeup_t wakeup,
329 php_persistent_handle_retire_t retire)
330 {
331 php_persistent_handle_factory_t *free_a = NULL;
332
333 if (!a) {
334 free_a = a = emalloc(sizeof(*a));
335 }
336 memset(a, 0, sizeof(*a));
337
338 a->provider = zend_symtable_str_find_ptr(&PHP_RAPHF_G->persistent_handle.hash,
339 name_str, name_len);
340
341 if (a->provider) {
342 a->ident.str = estrndup(ident_str, ident_len);
343 a->ident.len = ident_len;
344
345 a->wakeup = wakeup;
346 a->retire = retire;
347
348 if (free_a) {
349 a->free_on_abandon = 1;
350 }
351 } else {
352 if (free_a) {
353 efree(free_a);
354 }
355 a = NULL;
356 }
357
358 #if PHP_RAPHF_DEBUG_PHANDLES
359 fprintf(stderr, "CONCEDE: %p %p (%s) (%s)\n", PHP_RAPHF_G,
360 a ? a->provider : NULL, name_str, ident_str);
361 #endif
362
363 return a;
364 }
365
366 void php_persistent_handle_abandon(php_persistent_handle_factory_t *a)
367 {
368 zend_bool f = a->free_on_abandon;
369
370 #if PHP_RAPHF_DEBUG_PHANDLES
371 fprintf(stderr, "ABANDON: %p\n", a->provider);
372 #endif
373
374 if (a->ident.str) {
375 efree(a->ident.str);
376 }
377 memset(a, 0, sizeof(*a));
378 if (f) {
379 efree(a);
380 }
381 }
382
383 void *php_persistent_handle_acquire(php_persistent_handle_factory_t *a, void *init_arg)
384 {
385 int key;
386 zval *p;
387 ulong index;
388 void *handle = NULL;
389 php_persistent_handle_list_t *list;
390
391 list = php_persistent_handle_list_find(a->provider, a->ident.str, a->ident.len);
392 if (list) {
393 zend_hash_internal_pointer_end(&list->free);
394 key = zend_hash_get_current_key(&list->free, NULL, &index, 0);
395 p = zend_hash_get_current_data(&list->free);
396 if (p && HASH_KEY_NON_EXISTENT != key) {
397 handle = Z_PTR_P(p);
398 if (a->wakeup) {
399 a->wakeup(a, &handle);
400 }
401 zend_hash_index_del(&list->free, index);
402 } else {
403 handle = php_resource_factory_handle_ctor(&a->provider->rf, init_arg);
404 }
405 #if PHP_RAPHF_DEBUG_PHANDLES
406 fprintf(stderr, "CREATED: %p\n", handle);
407 #endif
408 if (handle) {
409 ++a->provider->list.used;
410 ++list->used;
411 }
412 }
413
414 return handle;
415 }
416
417 void *php_persistent_handle_accrete(php_persistent_handle_factory_t *a, void *handle)
418 {
419 void *new_handle = NULL;
420 php_persistent_handle_list_t *list;
421
422 new_handle = php_resource_factory_handle_copy(&a->provider->rf, handle);
423 if (handle) {
424 list = php_persistent_handle_list_find(a->provider, a->ident.str, a->ident.len);
425 if (list) {
426 ++list->used;
427 }
428 ++a->provider->list.used;
429 }
430
431 return new_handle;
432 }
433
434 void php_persistent_handle_release(php_persistent_handle_factory_t *a, void *handle)
435 {
436 php_persistent_handle_list_t *list;
437
438 list = php_persistent_handle_list_find(a->provider, a->ident.str, a->ident.len);
439 if (list) {
440 if (a->provider->list.used >= PHP_RAPHF_G->persistent_handle.limit) {
441 #if PHP_RAPHF_DEBUG_PHANDLES
442 fprintf(stderr, "DESTROY: %p\n", handle);
443 #endif
444 php_resource_factory_handle_dtor(&a->provider->rf, handle);
445 } else {
446 if (a->retire) {
447 a->retire(a, &handle);
448 }
449 zend_hash_next_index_insert_ptr(&list->free, handle);
450 }
451
452 --a->provider->list.used;
453 --list->used;
454 }
455 }
456
457 void php_persistent_handle_cleanup(const char *name_str, size_t name_len,
458 const char *ident_str, size_t ident_len)
459 {
460 php_persistent_handle_provider_t *provider;
461 php_persistent_handle_list_t *list;
462
463 if (name_str && name_len) {
464 provider = zend_symtable_str_find_ptr(&PHP_RAPHF_G->persistent_handle.hash,
465 name_str, name_len);
466
467 if (provider) {
468 if (ident_str && ident_len) {
469 list = php_persistent_handle_list_find(provider, ident_str,
470 ident_len);
471 if (list) {
472 zend_hash_apply_with_argument(&list->free,
473 php_persistent_handle_apply_cleanup_ex,
474 &provider->rf);
475 }
476 } else {
477 zend_hash_apply_with_argument(&provider->list.free,
478 php_persistent_handle_apply_cleanup,
479 &provider->rf);
480 }
481 }
482 } else {
483 zend_hash_apply_with_arguments(
484 &PHP_RAPHF_G->persistent_handle.hash,
485 php_persistent_handle_apply_cleanup_all, 2, ident_str,
486 ident_len);
487 }
488 }
489
490 HashTable *php_persistent_handle_statall(HashTable *ht)
491 {
492 if (zend_hash_num_elements(&PHP_RAPHF_G->persistent_handle.hash)) {
493 if (!ht) {
494 ALLOC_HASHTABLE(ht);
495 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
496 }
497 zend_hash_apply_with_arguments(
498 &PHP_RAPHF_G->persistent_handle.hash,
499 php_persistent_handle_apply_statall, 1, ht);
500 } else if (ht) {
501 ht = NULL;
502 }
503
504 return ht;
505 }
506
507 static php_resource_factory_ops_t php_persistent_handle_resource_factory_ops = {
508 (php_resource_factory_handle_ctor_t) php_persistent_handle_acquire,
509 (php_resource_factory_handle_copy_t) php_persistent_handle_accrete,
510 (php_resource_factory_handle_dtor_t) php_persistent_handle_release
511 };
512
513 php_resource_factory_ops_t *php_persistent_handle_get_resource_factory_ops(void)
514 {
515 return &php_persistent_handle_resource_factory_ops;
516 }
517
518 ZEND_BEGIN_ARG_INFO_EX(ai_raphf_stat_persistent_handles, 0, 0, 0)
519 ZEND_END_ARG_INFO();
520 static PHP_FUNCTION(raphf_stat_persistent_handles)
521 {
522 if (SUCCESS == zend_parse_parameters_none()) {
523 object_init(return_value);
524 if (php_persistent_handle_statall(HASH_OF(return_value))) {
525 return;
526 }
527 zval_dtor(return_value);
528 }
529 RETURN_FALSE;
530 }
531
532 ZEND_BEGIN_ARG_INFO_EX(ai_raphf_clean_persistent_handles, 0, 0, 0)
533 ZEND_ARG_INFO(0, name)
534 ZEND_ARG_INFO(0, ident)
535 ZEND_END_ARG_INFO();
536 static PHP_FUNCTION(raphf_clean_persistent_handles)
537 {
538 char *name_str = NULL, *ident_str = NULL;
539 int name_len = 0, ident_len = 0;
540
541 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!",
542 &name_str, &name_len, &ident_str, &ident_len)) {
543 php_persistent_handle_cleanup(name_str, name_len, ident_str, ident_len);
544 }
545 }
546
547 #if PHP_RAPHF_TEST
548 # include "php_raphf_test.c"
549 #endif
550
551 static const zend_function_entry raphf_functions[] = {
552 ZEND_NS_FENTRY("raphf", stat_persistent_handles,
553 ZEND_FN(raphf_stat_persistent_handles),
554 ai_raphf_stat_persistent_handles, 0)
555 ZEND_NS_FENTRY("raphf", clean_persistent_handles,
556 ZEND_FN(raphf_clean_persistent_handles),
557 ai_raphf_clean_persistent_handles, 0)
558 #if PHP_RAPHF_TEST
559 ZEND_NS_FENTRY("raphf", provide, ZEND_FN(raphf_provide), NULL, 0)
560 ZEND_NS_FENTRY("raphf", conceal, ZEND_FN(raphf_conceal), NULL, 0)
561 ZEND_NS_FENTRY("raphf", concede, ZEND_FN(raphf_concede), NULL, 0)
562 ZEND_NS_FENTRY("raphf", dispute, ZEND_FN(raphf_dispute), NULL, 0)
563 ZEND_NS_FENTRY("raphf", handle_ctor, ZEND_FN(raphf_handle_ctor), NULL, 0)
564 ZEND_NS_FENTRY("raphf", handle_copy, ZEND_FN(raphf_handle_copy), NULL, 0)
565 ZEND_NS_FENTRY("raphf", handle_dtor, ZEND_FN(raphf_handle_dtor), NULL, 0)
566 #endif
567 {0}
568 };
569
570 PHP_INI_BEGIN()
571 STD_PHP_INI_ENTRY("raphf.persistent_handle.limit", "-1", PHP_INI_SYSTEM,
572 OnUpdateLong, persistent_handle.limit, zend_raphf_globals,
573 raphf_globals)
574 PHP_INI_END()
575
576 static HashTable *php_persistent_handles_global_hash;
577
578 static PHP_GINIT_FUNCTION(raphf)
579 {
580 raphf_globals->persistent_handle.limit = -1;
581
582 zend_hash_init(&raphf_globals->persistent_handle.hash, 0, NULL,
583 php_persistent_handle_hash_dtor, 1);
584 if (php_persistent_handles_global_hash) {
585 zend_hash_copy(&raphf_globals->persistent_handle.hash,
586 php_persistent_handles_global_hash, NULL);
587 }
588 }
589
590 static PHP_GSHUTDOWN_FUNCTION(raphf)
591 {
592 zend_hash_destroy(&raphf_globals->persistent_handle.hash);
593 }
594
595 PHP_MINIT_FUNCTION(raphf)
596 {
597 php_persistent_handles_global_hash = &PHP_RAPHF_G->persistent_handle.hash;
598
599 #if PHP_RAPHF_TEST
600 PHP_MINIT(raphf_test)(INIT_FUNC_ARGS_PASSTHRU);
601 #endif
602
603 REGISTER_INI_ENTRIES();
604 return SUCCESS;
605 }
606
607 PHP_MSHUTDOWN_FUNCTION(raphf)
608 {
609 #if PHP_RAPHF_TEST
610 PHP_MSHUTDOWN(raphf_test)(SHUTDOWN_FUNC_ARGS_PASSTHRU);
611 #endif
612
613 UNREGISTER_INI_ENTRIES();
614 php_persistent_handles_global_hash = NULL;
615 return SUCCESS;
616 }
617
618 static int php_persistent_handle_apply_info_ex(zval *p, int argc,
619 va_list argv, zend_hash_key *key)
620 {
621 php_persistent_handle_list_t *list = Z_PTR_P(p);
622 zend_hash_key *super_key = va_arg(argv, zend_hash_key *);
623 char used[21], free[21];
624
625 slprintf(used, sizeof(used), "%u", list->used);
626 slprintf(free, sizeof(free), "%d", zend_hash_num_elements(&list->free));
627
628 php_info_print_table_row(4, super_key->key->val, key->key->val, used, free);
629
630 return ZEND_HASH_APPLY_KEEP;
631 }
632
633 static int php_persistent_handle_apply_info(zval *p, int argc,
634 va_list argv, zend_hash_key *key)
635 {
636 php_persistent_handle_provider_t *provider = Z_PTR_P(p);
637
638 zend_hash_apply_with_arguments(&provider->list.free,
639 php_persistent_handle_apply_info_ex, 1, key);
640
641 return ZEND_HASH_APPLY_KEEP;
642 }
643
644 PHP_MINFO_FUNCTION(raphf)
645 {
646 php_info_print_table_start();
647 php_info_print_table_header(2,
648 "Resource and persistent handle factory support", "enabled");
649 php_info_print_table_row(2, "Extension version", PHP_RAPHF_VERSION);
650 php_info_print_table_end();
651
652 php_info_print_table_start();
653 php_info_print_table_colspan_header(4, "Persistent handles in this "
654 #ifdef ZTS
655 "thread"
656 #else
657 "process"
658 #endif
659 );
660 php_info_print_table_header(4, "Provider", "Ident", "Used", "Free");
661 zend_hash_apply_with_arguments(
662 &PHP_RAPHF_G->persistent_handle.hash,
663 php_persistent_handle_apply_info, 0);
664 php_info_print_table_end();
665
666 DISPLAY_INI_ENTRIES();
667 }
668
669 zend_module_entry raphf_module_entry = {
670 STANDARD_MODULE_HEADER,
671 "raphf",
672 raphf_functions,
673 PHP_MINIT(raphf),
674 PHP_MSHUTDOWN(raphf),
675 NULL,
676 NULL,
677 PHP_MINFO(raphf),
678 PHP_RAPHF_VERSION,
679 ZEND_MODULE_GLOBALS(raphf),
680 PHP_GINIT(raphf),
681 PHP_GSHUTDOWN(raphf),
682 NULL,
683 STANDARD_MODULE_PROPERTIES_EX
684 };
685 /* }}} */
686
687 #ifdef COMPILE_DL_RAPHF
688 ZEND_GET_MODULE(raphf)
689 #endif
690
691 /*
692 * Local variables:
693 * tab-width: 4
694 * c-basic-offset: 4
695 * End:
696 * vim600: noet sw=4 ts=4 fdm=marker
697 * vim<600: noet sw=4 ts=4
698 */