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