1: /*
2: +----------------------------------------------------------------------+
3: | Zend Engine |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1998-2014 Zend Technologies Ltd. (http://www.zend.com) |
6: +----------------------------------------------------------------------+
7: | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
11: | If you did not receive a copy of the Zend license and are unable to |
12: | obtain it through the world-wide-web, please send a note to |
13: | license@zend.com so we can mail you a copy immediately. |
14: +----------------------------------------------------------------------+
15: | Author: Zeev Suraski <zeev@zend.com> |
16: +----------------------------------------------------------------------+
17: */
18:
19: /* $Id: zend_ini.c,v 1.1.1.4 2014/06/15 20:04:03 misho Exp $ */
20:
21: #include "zend.h"
22: #include "zend_qsort.h"
23: #include "zend_API.h"
24: #include "zend_ini.h"
25: #include "zend_alloc.h"
26: #include "zend_operators.h"
27: #include "zend_strtod.h"
28:
29: static HashTable *registered_zend_ini_directives;
30:
31: #define NO_VALUE_PLAINTEXT "no value"
32: #define NO_VALUE_HTML "<i>no value</i>"
33:
34: /*
35: * hash_apply functions
36: */
37: static int zend_remove_ini_entries(zend_ini_entry *ini_entry, int *module_number TSRMLS_DC) /* {{{ */
38: {
39: if (ini_entry->module_number == *module_number) {
40: return 1;
41: } else {
42: return 0;
43: }
44: }
45: /* }}} */
46:
47: static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage TSRMLS_DC) /* {{{ */
48: {
49: int result = FAILURE;
50:
51: if (ini_entry->modified) {
52: if (ini_entry->on_modify) {
53: zend_try {
54: /* even if on_modify bails out, we have to continue on with restoring,
55: since there can be allocated variables that would be freed on MM shutdown
56: and would lead to memory corruption later ini entry is modified again */
57: result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->orig_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC);
58: } zend_end_try();
59: }
60: if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
61: /* runtime failure is OK */
62: return 1;
63: }
64: if (ini_entry->value != ini_entry->orig_value) {
65: efree(ini_entry->value);
66: }
67: ini_entry->value = ini_entry->orig_value;
68: ini_entry->value_length = ini_entry->orig_value_length;
69: ini_entry->modifiable = ini_entry->orig_modifiable;
70: ini_entry->modified = 0;
71: ini_entry->orig_value = NULL;
72: ini_entry->orig_value_length = 0;
73: ini_entry->orig_modifiable = 0;
74: }
75: return 0;
76: }
77: /* }}} */
78:
79: static int zend_restore_ini_entry_wrapper(zend_ini_entry **ini_entry TSRMLS_DC) /* {{{ */
80: {
81: zend_restore_ini_entry_cb(*ini_entry, ZEND_INI_STAGE_DEACTIVATE TSRMLS_CC);
82: return 1;
83: }
84: /* }}} */
85:
86: /*
87: * Startup / shutdown
88: */
89: ZEND_API int zend_ini_startup(TSRMLS_D) /* {{{ */
90: {
91: registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable));
92:
93: EG(ini_directives) = registered_zend_ini_directives;
94: EG(modified_ini_directives) = NULL;
95: EG(error_reporting_ini_entry) = NULL;
96: if (zend_hash_init_ex(registered_zend_ini_directives, 100, NULL, NULL, 1, 0) == FAILURE) {
97: return FAILURE;
98: }
99: return SUCCESS;
100: }
101: /* }}} */
102:
103: ZEND_API int zend_ini_shutdown(TSRMLS_D) /* {{{ */
104: {
105: zend_hash_destroy(EG(ini_directives));
106: free(EG(ini_directives));
107: return SUCCESS;
108: }
109: /* }}} */
110:
111: ZEND_API int zend_ini_global_shutdown(TSRMLS_D) /* {{{ */
112: {
113: zend_hash_destroy(registered_zend_ini_directives);
114: free(registered_zend_ini_directives);
115: return SUCCESS;
116: }
117: /* }}} */
118:
119: ZEND_API int zend_ini_deactivate(TSRMLS_D) /* {{{ */
120: {
121: if (EG(modified_ini_directives)) {
122: zend_hash_apply(EG(modified_ini_directives), (apply_func_t) zend_restore_ini_entry_wrapper TSRMLS_CC);
123: zend_hash_destroy(EG(modified_ini_directives));
124: FREE_HASHTABLE(EG(modified_ini_directives));
125: EG(modified_ini_directives) = NULL;
126: }
127: return SUCCESS;
128: }
129: /* }}} */
130:
131: #ifdef ZTS
132: ZEND_API int zend_copy_ini_directives(TSRMLS_D) /* {{{ */
133: {
134: zend_ini_entry ini_entry;
135:
136: EG(modified_ini_directives) = NULL;
137: EG(error_reporting_ini_entry) = NULL;
138: EG(ini_directives) = (HashTable *) malloc(sizeof(HashTable));
139: if (zend_hash_init_ex(EG(ini_directives), registered_zend_ini_directives->nNumOfElements, NULL, NULL, 1, 0) == FAILURE) {
140: return FAILURE;
141: }
142: zend_hash_copy(EG(ini_directives), registered_zend_ini_directives, NULL, &ini_entry, sizeof(zend_ini_entry));
143: return SUCCESS;
144: }
145: /* }}} */
146: #endif
147:
148: static int ini_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
149: {
150: const Bucket *f;
151: const Bucket *s;
152:
153: f = *((const Bucket **) a);
154: s = *((const Bucket **) b);
155:
156: if (f->nKeyLength == 0 && s->nKeyLength == 0) { /* both numeric */
157: return ZEND_NORMALIZE_BOOL(f->nKeyLength - s->nKeyLength);
158: } else if (f->nKeyLength == 0) { /* f is numeric, s is not */
159: return -1;
160: } else if (s->nKeyLength == 0) { /* s is numeric, f is not */
161: return 1;
162: } else { /* both strings */
163: return zend_binary_strcasecmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength);
164: }
165: }
166: /* }}} */
167:
168: ZEND_API void zend_ini_sort_entries(TSRMLS_D) /* {{{ */
169: {
170: zend_hash_sort(EG(ini_directives), zend_qsort, ini_key_compare, 0 TSRMLS_CC);
171: }
172: /* }}} */
173:
174: /*
175: * Registration / unregistration
176: */
177: ZEND_API int zend_register_ini_entries(const zend_ini_entry *ini_entry, int module_number TSRMLS_DC) /* {{{ */
178: {
179: const zend_ini_entry *p = ini_entry;
180: zend_ini_entry *hashed_ini_entry;
181: zval default_value;
182: HashTable *directives = registered_zend_ini_directives;
183: zend_bool config_directive_success = 0;
184:
185: #ifdef ZTS
186: /* if we are called during the request, eg: from dl(),
187: * then we should not touch the global directives table,
188: * and should update the per-(request|thread) version instead.
189: * This solves two problems: one is that ini entries for dl()'d
190: * extensions will now work, and the second is that updating the
191: * global hash here from dl() is not mutex protected and can
192: * lead to death.
193: */
194: if (directives != EG(ini_directives)) {
195: directives = EG(ini_directives);
196: }
197: #endif
198:
199: while (p->name) {
200: config_directive_success = 0;
201: if (zend_hash_add(directives, p->name, p->name_length, (void*)p, sizeof(zend_ini_entry), (void **) &hashed_ini_entry) == FAILURE) {
202: zend_unregister_ini_entries(module_number TSRMLS_CC);
203: return FAILURE;
204: }
205: hashed_ini_entry->module_number = module_number;
206: if ((zend_get_configuration_directive(p->name, p->name_length, &default_value)) == SUCCESS) {
207: if (!hashed_ini_entry->on_modify
208: || hashed_ini_entry->on_modify(hashed_ini_entry, Z_STRVAL(default_value), Z_STRLEN(default_value), hashed_ini_entry->mh_arg1, hashed_ini_entry->mh_arg2, hashed_ini_entry->mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC) == SUCCESS) {
209: hashed_ini_entry->value = Z_STRVAL(default_value);
210: hashed_ini_entry->value_length = Z_STRLEN(default_value);
211: config_directive_success = 1;
212: }
213: }
214:
215: if (!config_directive_success && hashed_ini_entry->on_modify) {
216: hashed_ini_entry->on_modify(hashed_ini_entry, hashed_ini_entry->value, hashed_ini_entry->value_length, hashed_ini_entry->mh_arg1, hashed_ini_entry->mh_arg2, hashed_ini_entry->mh_arg3, ZEND_INI_STAGE_STARTUP TSRMLS_CC);
217: }
218: p++;
219: }
220: return SUCCESS;
221: }
222: /* }}} */
223:
224: ZEND_API void zend_unregister_ini_entries(int module_number TSRMLS_DC) /* {{{ */
225: {
226: zend_hash_apply_with_argument(registered_zend_ini_directives, (apply_func_arg_t) zend_remove_ini_entries, (void *) &module_number TSRMLS_CC);
227: }
228: /* }}} */
229:
230: #ifdef ZTS
231: static int zend_ini_refresh_cache(zend_ini_entry *p, int stage TSRMLS_DC) /* {{{ */
232: {
233: if (p->on_modify) {
234: p->on_modify(p, p->value, p->value_length, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage TSRMLS_CC);
235: }
236: return 0;
237: }
238: /* }}} */
239:
240: ZEND_API void zend_ini_refresh_caches(int stage TSRMLS_DC) /* {{{ */
241: {
242: zend_hash_apply_with_argument(EG(ini_directives), (apply_func_arg_t) zend_ini_refresh_cache, (void *)(zend_intptr_t) stage TSRMLS_CC);
243: }
244: /* }}} */
245: #endif
246:
247: ZEND_API int zend_alter_ini_entry(char *name, uint name_length, char *new_value, uint new_value_length, int modify_type, int stage) /* {{{ */
248: {
249: TSRMLS_FETCH();
250:
251: return zend_alter_ini_entry_ex(name, name_length, new_value, new_value_length, modify_type, stage, 0 TSRMLS_CC);
252: }
253: /* }}} */
254:
255: ZEND_API int zend_alter_ini_entry_ex(char *name, uint name_length, char *new_value, uint new_value_length, int modify_type, int stage, int force_change TSRMLS_DC) /* {{{ */
256: {
257: zend_ini_entry *ini_entry;
258: char *duplicate;
259: zend_bool modifiable;
260: zend_bool modified;
261:
262: if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
263: return FAILURE;
264: }
265:
266: modifiable = ini_entry->modifiable;
267: modified = ini_entry->modified;
268:
269: if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
270: ini_entry->modifiable = ZEND_INI_SYSTEM;
271: }
272:
273: if (!force_change) {
274: if (!(ini_entry->modifiable & modify_type)) {
275: return FAILURE;
276: }
277: }
278:
279: if (!EG(modified_ini_directives)) {
280: ALLOC_HASHTABLE(EG(modified_ini_directives));
281: zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
282: }
283: if (!modified) {
284: ini_entry->orig_value = ini_entry->value;
285: ini_entry->orig_value_length = ini_entry->value_length;
286: ini_entry->orig_modifiable = modifiable;
287: ini_entry->modified = 1;
288: zend_hash_add(EG(modified_ini_directives), name, name_length, &ini_entry, sizeof(zend_ini_entry*), NULL);
289: }
290:
291: duplicate = estrndup(new_value, new_value_length);
292:
293: if (!ini_entry->on_modify
294: || ini_entry->on_modify(ini_entry, duplicate, new_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC) == SUCCESS) {
295: if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
296: efree(ini_entry->value);
297: }
298: ini_entry->value = duplicate;
299: ini_entry->value_length = new_value_length;
300: } else {
301: efree(duplicate);
302: return FAILURE;
303: }
304:
305: return SUCCESS;
306: }
307: /* }}} */
308:
309: ZEND_API int zend_restore_ini_entry(char *name, uint name_length, int stage) /* {{{ */
310: {
311: zend_ini_entry *ini_entry;
312: TSRMLS_FETCH();
313:
314: if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE ||
315: (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
316: return FAILURE;
317: }
318:
319: if (EG(modified_ini_directives)) {
320: if (zend_restore_ini_entry_cb(ini_entry, stage TSRMLS_CC) == 0) {
321: zend_hash_del(EG(modified_ini_directives), name, name_length);
322: } else {
323: return FAILURE;
324: }
325: }
326:
327: return SUCCESS;
328: }
329: /* }}} */
330:
331: ZEND_API int zend_ini_register_displayer(char *name, uint name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
332: {
333: zend_ini_entry *ini_entry;
334:
335: if (zend_hash_find(registered_zend_ini_directives, name, name_length, (void **) &ini_entry) == FAILURE) {
336: return FAILURE;
337: }
338:
339: ini_entry->displayer = displayer;
340: return SUCCESS;
341: }
342: /* }}} */
343:
344: /*
345: * Data retrieval
346: */
347:
348: ZEND_API long zend_ini_long(char *name, uint name_length, int orig) /* {{{ */
349: {
350: zend_ini_entry *ini_entry;
351: TSRMLS_FETCH();
352:
353: if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == SUCCESS) {
354: if (orig && ini_entry->modified) {
355: return (ini_entry->orig_value ? strtol(ini_entry->orig_value, NULL, 0) : 0);
356: } else {
357: return (ini_entry->value ? strtol(ini_entry->value, NULL, 0) : 0);
358: }
359: }
360:
361: return 0;
362: }
363: /* }}} */
364:
365: ZEND_API double zend_ini_double(char *name, uint name_length, int orig) /* {{{ */
366: {
367: zend_ini_entry *ini_entry;
368: TSRMLS_FETCH();
369:
370: if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == SUCCESS) {
371: if (orig && ini_entry->modified) {
372: return (double) (ini_entry->orig_value ? zend_strtod(ini_entry->orig_value, NULL) : 0.0);
373: } else {
374: return (double) (ini_entry->value ? zend_strtod(ini_entry->value, NULL) : 0.0);
375: }
376: }
377:
378: return 0.0;
379: }
380: /* }}} */
381:
382: ZEND_API char *zend_ini_string_ex(char *name, uint name_length, int orig, zend_bool *exists) /* {{{ */
383: {
384: zend_ini_entry *ini_entry;
385: TSRMLS_FETCH();
386:
387: if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == SUCCESS) {
388: if (exists) {
389: *exists = 1;
390: }
391:
392: if (orig && ini_entry->modified) {
393: return ini_entry->orig_value;
394: } else {
395: return ini_entry->value;
396: }
397: } else {
398: if (exists) {
399: *exists = 0;
400: }
401: return NULL;
402: }
403: }
404: /* }}} */
405:
406: ZEND_API char *zend_ini_string(char *name, uint name_length, int orig) /* {{{ */
407: {
408: zend_bool exists = 1;
409: char *return_value;
410:
411: return_value = zend_ini_string_ex(name, name_length, orig, &exists);
412: if (!exists) {
413: return NULL;
414: } else if (!return_value) {
415: return_value = "";
416: }
417: return return_value;
418: }
419: /* }}} */
420:
421: #if TONY_20070307
422: static void zend_ini_displayer_cb(zend_ini_entry *ini_entry, int type) /* {{{ */
423: {
424: if (ini_entry->displayer) {
425: ini_entry->displayer(ini_entry, type);
426: } else {
427: char *display_string;
428: uint display_string_length;
429:
430: if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
431: if (ini_entry->orig_value) {
432: display_string = ini_entry->orig_value;
433: display_string_length = ini_entry->orig_value_length;
434: } else {
435: if (zend_uv.html_errors) {
436: display_string = NO_VALUE_HTML;
437: display_string_length = sizeof(NO_VALUE_HTML) - 1;
438: } else {
439: display_string = NO_VALUE_PLAINTEXT;
440: display_string_length = sizeof(NO_VALUE_PLAINTEXT) - 1;
441: }
442: }
443: } else if (ini_entry->value && ini_entry->value[0]) {
444: display_string = ini_entry->value;
445: display_string_length = ini_entry->value_length;
446: } else {
447: if (zend_uv.html_errors) {
448: display_string = NO_VALUE_HTML;
449: display_string_length = sizeof(NO_VALUE_HTML) - 1;
450: } else {
451: display_string = NO_VALUE_PLAINTEXT;
452: display_string_length = sizeof(NO_VALUE_PLAINTEXT) - 1;
453: }
454: }
455: ZEND_WRITE(display_string, display_string_length);
456: }
457: }
458: /* }}} */
459: #endif
460:
461: ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
462: {
463: int value, tmp_value_len;
464: char *tmp_value;
465:
466: if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
467: tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
468: tmp_value_len = ini_entry->orig_value_length;
469: } else if (ini_entry->value) {
470: tmp_value = ini_entry->value;
471: tmp_value_len = ini_entry->value_length;
472: } else {
473: tmp_value = NULL;
474: tmp_value_len = 0;
475: }
476:
477: if (tmp_value) {
478: if (tmp_value_len == 4 && strcasecmp(tmp_value, "true") == 0) {
479: value = 1;
480: } else if (tmp_value_len == 3 && strcasecmp(tmp_value, "yes") == 0) {
481: value = 1;
482: } else if (tmp_value_len == 2 && strcasecmp(tmp_value, "on") == 0) {
483: value = 1;
484: } else {
485: value = atoi(tmp_value);
486: }
487: } else {
488: value = 0;
489: }
490:
491: if (value) {
492: ZEND_PUTS("On");
493: } else {
494: ZEND_PUTS("Off");
495: }
496: }
497: /* }}} */
498:
499: ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
500: {
501: char *value;
502:
503: if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
504: value = ini_entry->orig_value;
505: } else if (ini_entry->value) {
506: value = ini_entry->value;
507: } else {
508: value = NULL;
509: }
510: if (value) {
511: if (zend_uv.html_errors) {
512: zend_printf("<font style=\"color: %s\">%s</font>", value, value);
513: } else {
514: ZEND_PUTS(value);
515: }
516: } else {
517: if (zend_uv.html_errors) {
518: ZEND_PUTS(NO_VALUE_HTML);
519: } else {
520: ZEND_PUTS(NO_VALUE_PLAINTEXT);
521: }
522: }
523: }
524: /* }}} */
525:
526: ZEND_INI_DISP(display_link_numbers) /* {{{ */
527: {
528: char *value;
529:
530: if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
531: value = ini_entry->orig_value;
532: } else if (ini_entry->value) {
533: value = ini_entry->value;
534: } else {
535: value = NULL;
536: }
537:
538: if (value) {
539: if (atoi(value) == -1) {
540: ZEND_PUTS("Unlimited");
541: } else {
542: zend_printf("%s", value);
543: }
544: }
545: }
546: /* }}} */
547:
548: /* Standard message handlers */
549: ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
550: {
551: zend_bool *p;
552: #ifndef ZTS
553: char *base = (char *) mh_arg2;
554: #else
555: char *base;
556:
557: base = (char *) ts_resource(*((int *) mh_arg2));
558: #endif
559:
560: p = (zend_bool *) (base+(size_t) mh_arg1);
561:
562: if (new_value_length == 2 && strcasecmp("on", new_value) == 0) {
563: *p = (zend_bool) 1;
564: }
565: else if (new_value_length == 3 && strcasecmp("yes", new_value) == 0) {
566: *p = (zend_bool) 1;
567: }
568: else if (new_value_length == 4 && strcasecmp("true", new_value) == 0) {
569: *p = (zend_bool) 1;
570: }
571: else {
572: *p = (zend_bool) atoi(new_value);
573: }
574: return SUCCESS;
575: }
576: /* }}} */
577:
578: ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
579: {
580: long *p;
581: #ifndef ZTS
582: char *base = (char *) mh_arg2;
583: #else
584: char *base;
585:
586: base = (char *) ts_resource(*((int *) mh_arg2));
587: #endif
588:
589: p = (long *) (base+(size_t) mh_arg1);
590:
591: *p = zend_atol(new_value, new_value_length);
592: return SUCCESS;
593: }
594: /* }}} */
595:
596: ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
597: {
598: long *p, tmp;
599: #ifndef ZTS
600: char *base = (char *) mh_arg2;
601: #else
602: char *base;
603:
604: base = (char *) ts_resource(*((int *) mh_arg2));
605: #endif
606:
607: tmp = zend_atol(new_value, new_value_length);
608: if (tmp < 0) {
609: return FAILURE;
610: }
611:
612: p = (long *) (base+(size_t) mh_arg1);
613: *p = tmp;
614:
615: return SUCCESS;
616: }
617: /* }}} */
618:
619: ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
620: {
621: double *p;
622: #ifndef ZTS
623: char *base = (char *) mh_arg2;
624: #else
625: char *base;
626:
627: base = (char *) ts_resource(*((int *) mh_arg2));
628: #endif
629:
630: p = (double *) (base+(size_t) mh_arg1);
631:
632: *p = zend_strtod(new_value, NULL);
633: return SUCCESS;
634: }
635: /* }}} */
636:
637: ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
638: {
639: char **p;
640: #ifndef ZTS
641: char *base = (char *) mh_arg2;
642: #else
643: char *base;
644:
645: base = (char *) ts_resource(*((int *) mh_arg2));
646: #endif
647:
648: p = (char **) (base+(size_t) mh_arg1);
649:
650: *p = new_value;
651: return SUCCESS;
652: }
653: /* }}} */
654:
655: ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
656: {
657: char **p;
658: #ifndef ZTS
659: char *base = (char *) mh_arg2;
660: #else
661: char *base;
662:
663: base = (char *) ts_resource(*((int *) mh_arg2));
664: #endif
665:
666: if (new_value && !new_value[0]) {
667: return FAILURE;
668: }
669:
670: p = (char **) (base+(size_t) mh_arg1);
671:
672: *p = new_value;
673: return SUCCESS;
674: }
675: /* }}} */
676:
677: /*
678: * Local variables:
679: * tab-width: 4
680: * c-basic-offset: 4
681: * indent-tabs-mode: t
682: * End:
683: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>