Annotation of embedaddon/php/main/streams/filter.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:    | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       6:    +----------------------------------------------------------------------+
                      7:    | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
                     11:    | If you did not receive a copy of the PHP license and are unable to   |
                     12:    | obtain it through the world-wide-web, please send a note to          |
                     13:    | license@php.net so we can mail you a copy immediately.               |
                     14:    +----------------------------------------------------------------------+
                     15:    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
                     16:    +----------------------------------------------------------------------+
                     17:  */
                     18: 
1.1.1.2   misho      19: /* $Id$ */
1.1       misho      20: 
                     21: #include "php.h"
                     22: #include "php_globals.h"
                     23: #include "php_network.h"
                     24: #include "php_open_temporary_file.h"
                     25: #include "ext/standard/file.h"
                     26: #include <stddef.h>
                     27: #include <fcntl.h>
                     28: 
                     29: #include "php_streams_int.h"
                     30: 
                     31: /* Global filter hash, copied to FG(stream_filters) on registration of volatile filter */
                     32: static HashTable stream_filters_hash;
                     33: 
                     34: /* Should only be used during core initialization */
                     35: PHPAPI HashTable *php_get_stream_filters_hash_global(void)
                     36: {
                     37:        return &stream_filters_hash;
                     38: }
                     39: 
                     40: /* Normal hash selection/retrieval call */
                     41: PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D)
                     42: {
                     43:        return (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
                     44: }
                     45: 
                     46: /* API for registering GLOBAL filters */
                     47: PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
                     48: {
                     49:        return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL);
                     50: }
                     51: 
                     52: PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC)
                     53: {
                     54:        return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1);
                     55: }
                     56: 
                     57: /* API for registering VOLATILE wrappers */
                     58: PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
                     59: {
                     60:        if (!FG(stream_filters)) {
                     61:                php_stream_filter_factory tmpfactory;
                     62: 
                     63:                ALLOC_HASHTABLE(FG(stream_filters));
                     64:                zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1);
                     65:                zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL, &tmpfactory, sizeof(php_stream_filter_factory));
                     66:        }
                     67: 
                     68:        return zend_hash_add(FG(stream_filters), (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL);
                     69: }
                     70: 
                     71: /* Buckets */
                     72: 
                     73: PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC)
                     74: {
                     75:        int is_persistent = php_stream_is_persistent(stream);
                     76:        php_stream_bucket *bucket;
                     77: 
                     78:        bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
                     79: 
                     80:        if (bucket == NULL) {
                     81:                return NULL;
                     82:        }
                     83:        
                     84:        bucket->next = bucket->prev = NULL;
                     85: 
                     86:        if (is_persistent && !buf_persistent) {
                     87:                /* all data in a persistent bucket must also be persistent */
                     88:                bucket->buf = pemalloc(buflen, 1);
                     89:                
                     90:                if (bucket->buf == NULL) {
                     91:                        pefree(bucket, 1);
                     92:                        return NULL;
                     93:                }
                     94:                
                     95:                memcpy(bucket->buf, buf, buflen);
                     96:                bucket->buflen = buflen;
                     97:                bucket->own_buf = 1;
                     98:        } else {
                     99:                bucket->buf = buf;
                    100:                bucket->buflen = buflen;
                    101:                bucket->own_buf = own_buf;
                    102:        }
                    103:        bucket->is_persistent = is_persistent;
                    104:        bucket->refcount = 1;
                    105:        bucket->brigade = NULL;
                    106: 
                    107:        return bucket;
                    108: }
                    109: 
                    110: /* Given a bucket, returns a version of that bucket with a writeable buffer.
                    111:  * If the original bucket has a refcount of 1 and owns its buffer, then it
                    112:  * is returned unchanged.
                    113:  * Otherwise, a copy of the buffer is made.
                    114:  * In both cases, the original bucket is unlinked from its brigade.
                    115:  * If a copy is made, the original bucket is delref'd.
                    116:  * */
                    117: PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC)
                    118: {
                    119:        php_stream_bucket *retval;
                    120: 
                    121:        php_stream_bucket_unlink(bucket TSRMLS_CC);
                    122:        
                    123:        if (bucket->refcount == 1 && bucket->own_buf) {
                    124:                return bucket;
                    125:        }
                    126: 
                    127:        retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent);
                    128:        memcpy(retval, bucket, sizeof(*retval));
                    129: 
                    130:        retval->buf = pemalloc(retval->buflen, retval->is_persistent);
                    131:        memcpy(retval->buf, bucket->buf, retval->buflen);
                    132: 
                    133:        retval->refcount = 1;
                    134:        retval->own_buf = 1;
                    135: 
                    136:        php_stream_bucket_delref(bucket TSRMLS_CC);
                    137:        
                    138:        return retval;
                    139: }
                    140: 
                    141: PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC)
                    142: {
                    143:        *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
                    144:        *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
                    145: 
                    146:        if (*left == NULL || *right == NULL) {
                    147:                goto exit_fail;
                    148:        }
                    149: 
                    150:        (*left)->buf = pemalloc(length, in->is_persistent);
                    151:        (*left)->buflen = length;
                    152:        memcpy((*left)->buf, in->buf, length);
                    153:        (*left)->refcount = 1;
                    154:        (*left)->own_buf = 1;
                    155:        (*left)->is_persistent = in->is_persistent;
                    156:        
                    157:        (*right)->buflen = in->buflen - length;
                    158:        (*right)->buf = pemalloc((*right)->buflen, in->is_persistent);
                    159:        memcpy((*right)->buf, in->buf + length, (*right)->buflen);
                    160:        (*right)->refcount = 1;
                    161:        (*right)->own_buf = 1;
                    162:        (*right)->is_persistent = in->is_persistent;
                    163:        
                    164:        return SUCCESS;
                    165:        
                    166: exit_fail:
                    167:        if (*right) {
                    168:                if ((*right)->buf) {
                    169:                        pefree((*right)->buf, in->is_persistent);
                    170:                }
                    171:                pefree(*right, in->is_persistent);
                    172:        }
                    173:        if (*left) {
                    174:                if ((*left)->buf) {
                    175:                        pefree((*left)->buf, in->is_persistent);
                    176:                }
                    177:                pefree(*left, in->is_persistent);
                    178:        }
                    179:        return FAILURE;
                    180: }
                    181: 
                    182: PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC)
                    183: {
                    184:        if (--bucket->refcount == 0) {
                    185:                if (bucket->own_buf) {
                    186:                        pefree(bucket->buf, bucket->is_persistent);
                    187:                }
                    188:                pefree(bucket, bucket->is_persistent);
                    189:        }
                    190: }
                    191: 
                    192: PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
                    193: {
                    194:        bucket->next = brigade->head;
                    195:        bucket->prev = NULL;
                    196: 
                    197:        if (brigade->head) {
                    198:                brigade->head->prev = bucket;
                    199:        } else {
                    200:                brigade->tail = bucket;
                    201:        }
                    202:        brigade->head = bucket;
                    203:        bucket->brigade = brigade;
                    204: }
                    205: 
                    206: PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
                    207: {
                    208:        if (brigade->tail == bucket) {
                    209:                return;
                    210:        }
                    211: 
                    212:        bucket->prev = brigade->tail;
                    213:        bucket->next = NULL;
                    214: 
                    215:        if (brigade->tail) {
                    216:                brigade->tail->next = bucket;
                    217:        } else {
                    218:                brigade->head = bucket;
                    219:        }
                    220:        brigade->tail = bucket;
                    221:        bucket->brigade = brigade;
                    222: }
                    223: 
                    224: PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC)
                    225: {
                    226:        if (bucket->prev) {
                    227:                bucket->prev->next = bucket->next;
                    228:        } else if (bucket->brigade) {
                    229:                bucket->brigade->head = bucket->next;
                    230:        }
                    231:        if (bucket->next) {
                    232:                bucket->next->prev = bucket->prev;
                    233:        } else if (bucket->brigade) {
                    234:                bucket->brigade->tail = bucket->prev;
                    235:        }
                    236:        bucket->brigade = NULL;
                    237:        bucket->next = bucket->prev = NULL;
                    238: }
                    239:        
                    240: 
                    241: 
                    242: 
                    243: 
                    244: 
                    245: 
                    246: 
                    247: /* We allow very simple pattern matching for filter factories:
                    248:  * if "convert.charset.utf-8/sjis" is requested, we search first for an exact
                    249:  * match. If that fails, we try "convert.charset.*", then "convert.*"
                    250:  * This means that we don't need to clog up the hashtable with a zillion
                    251:  * charsets (for example) but still be able to provide them all as filters */
                    252: PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
                    253: {
                    254:        HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
                    255:        php_stream_filter_factory *factory = NULL;
                    256:        php_stream_filter *filter = NULL;
                    257:        int n;
                    258:        char *period;
                    259: 
                    260:        n = strlen(filtername);
                    261:        
                    262:        if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n + 1, (void**)&factory)) {
                    263:                filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
                    264:        } else if ((period = strrchr(filtername, '.'))) {
                    265:                /* try a wildcard */
                    266:                char *wildname;
                    267: 
                    268:                wildname = emalloc(n+3);
                    269:                memcpy(wildname, filtername, n+1);
                    270:                period = wildname + (period - filtername);
                    271:                while (period && !filter) {
                    272:                        *period = '\0';
                    273:                        strncat(wildname, ".*", 2);
                    274:                        if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname) + 1, (void**)&factory)) {
                    275:                                filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
                    276:                        }
                    277: 
                    278:                        *period = '\0';
                    279:                        period = strrchr(wildname, '.');
                    280:                }
                    281:                efree(wildname);
                    282:        }
                    283: 
                    284:        if (filter == NULL) {
                    285:                /* TODO: these need correct docrefs */
                    286:                if (factory == NULL)
                    287:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername);
                    288:                else
                    289:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
                    290:        }
                    291:        
                    292:        return filter;
                    293: }
                    294: 
                    295: PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC)
                    296: {
                    297:        php_stream_filter *filter;
                    298: 
                    299:        filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
                    300:        memset(filter, 0, sizeof(php_stream_filter));
                    301: 
                    302:        filter->fops = fops;
                    303:        filter->abstract = abstract;
                    304:        filter->is_persistent = persistent;
                    305:        
                    306:        return filter;
                    307: }
                    308: 
                    309: PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
                    310: {
                    311:        if (filter->fops->dtor)
                    312:                filter->fops->dtor(filter TSRMLS_CC);
                    313:        pefree(filter, filter->is_persistent);
                    314: }
                    315: 
                    316: PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
                    317: {
                    318:        filter->next = chain->head;
                    319:        filter->prev = NULL;
                    320: 
                    321:        if (chain->head) {
                    322:                chain->head->prev = filter;
                    323:        } else {
                    324:                chain->tail = filter;
                    325:        }
                    326:        chain->head = filter;
                    327:        filter->chain = chain;
                    328: 
                    329:        return SUCCESS;
                    330: }
                    331: 
                    332: PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
                    333: {
                    334:        php_stream_filter_prepend_ex(chain, filter TSRMLS_CC);
                    335: }
                    336: 
                    337: PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
                    338: {
                    339:        php_stream *stream = chain->stream;
                    340: 
                    341:        filter->prev = chain->tail;
                    342:        filter->next = NULL;
                    343:        if (chain->tail) {
                    344:                chain->tail->next = filter;
                    345:        } else {
                    346:                chain->head = filter;
                    347:        }
                    348:        chain->tail = filter;
                    349:        filter->chain = chain;
                    350: 
                    351:        if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) {
                    352:                /* Let's going ahead and wind anything in the buffer through this filter */
                    353:                php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
                    354:                php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
                    355:                php_stream_filter_status_t status;
                    356:                php_stream_bucket *bucket;
                    357:                size_t consumed = 0;
                    358: 
                    359:                bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
                    360:                php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
                    361:                status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC);
                    362: 
                    363:                if (stream->readpos + consumed > (uint)stream->writepos) {
                    364:                        /* No behaving filter should cause this. */
                    365:                        status = PSFS_ERR_FATAL;
                    366:                }
                    367: 
                    368:                switch (status) {
                    369:                        case PSFS_ERR_FATAL:
                    370:                                while (brig_in.head) {
                    371:                                        bucket = brig_in.head;
                    372:                                        php_stream_bucket_unlink(bucket TSRMLS_CC);
                    373:                                        php_stream_bucket_delref(bucket TSRMLS_CC);
                    374:                                }
                    375:                                while (brig_out.head) {
                    376:                                        bucket = brig_out.head;
                    377:                                        php_stream_bucket_unlink(bucket TSRMLS_CC);
                    378:                                        php_stream_bucket_delref(bucket TSRMLS_CC);
                    379:                                }
                    380:                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data");
                    381:                                return FAILURE;
                    382:                        case PSFS_FEED_ME:
                    383:                                /* We don't actually need data yet,
                    384:                                   leave this filter in a feed me state until data is needed. 
                    385:                                   Reset stream's internal read buffer since the filter is "holding" it. */
                    386:                                stream->readpos = 0;
                    387:                                stream->writepos = 0;
                    388:                                break;
                    389:                        case PSFS_PASS_ON:
                    390:                                /* If any data is consumed, we cannot rely upon the existing read buffer,
                    391:                                   as the filtered data must replace the existing data, so invalidate the cache */
                    392:                                /* note that changes here should be reflected in
                    393:                                   main/streams/streams.c::php_stream_fill_read_buffer */
                    394:                                stream->writepos = 0;
                    395:                                stream->readpos = 0;
                    396: 
                    397:                                while (brig_outp->head) {
                    398:                                        bucket = brig_outp->head;
                    399:                                        /* Grow buffer to hold this bucket if need be.
                    400:                                           TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
                    401:                                        if (stream->readbuflen - stream->writepos < bucket->buflen) {
                    402:                                                stream->readbuflen += bucket->buflen;
                    403:                                                stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
                    404:                                        }
                    405:                                        memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
                    406:                                        stream->writepos += bucket->buflen;
                    407: 
                    408:                                        php_stream_bucket_unlink(bucket TSRMLS_CC);
                    409:                                        php_stream_bucket_delref(bucket TSRMLS_CC);
                    410:                                }
                    411:                                break;
                    412:                }
                    413:        }
                    414: 
                    415:        return SUCCESS;
                    416: }
                    417: 
                    418: PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
                    419: {
                    420:        if (php_stream_filter_append_ex(chain, filter TSRMLS_CC) != SUCCESS) {
                    421:                if (chain->head == filter) {
                    422:                        chain->head = NULL;
                    423:                        chain->tail = NULL;
                    424:                } else {
                    425:                        filter->prev->next = NULL;
                    426:                        chain->tail = filter->prev;
                    427:                }
                    428:        }
                    429: }
                    430: 
                    431: PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC)
                    432: {
                    433:        php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
                    434:        php_stream_bucket *bucket;
                    435:        php_stream_filter_chain *chain;
                    436:        php_stream_filter *current;
                    437:        php_stream *stream;
                    438:        size_t flushed_size = 0;
                    439:        long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC);
                    440: 
                    441:        if (!filter->chain || !filter->chain->stream) {
                    442:                /* Filter is not attached to a chain, or chain is somehow not part of a stream */
                    443:                return FAILURE;
                    444:        }
                    445: 
                    446:        chain = filter->chain;
                    447:        stream = chain->stream;
                    448: 
                    449:        for(current = filter; current; current = current->next) {
                    450:                php_stream_filter_status_t status;
                    451: 
                    452:                status = filter->fops->filter(stream, filter, inp, outp, NULL, flags TSRMLS_CC);
                    453:                if (status == PSFS_FEED_ME) {
                    454:                        /* We've flushed the data far enough */
                    455:                        return SUCCESS;
                    456:                }
                    457:                if (status == PSFS_ERR_FATAL) {
                    458:                        return FAILURE;
                    459:                }
                    460:                /* Otherwise we have data available to PASS_ON
                    461:                        Swap the brigades and continue */
                    462:                brig_temp = inp;
                    463:                inp = outp;
                    464:                outp = brig_temp;
                    465:                outp->head = NULL;
                    466:                outp->tail = NULL;
                    467: 
                    468:                flags = PSFS_FLAG_NORMAL;
                    469:        }
                    470: 
                    471:        /* Last filter returned data via PSFS_PASS_ON
                    472:                Do something with it */
                    473: 
                    474:        for(bucket = inp->head; bucket; bucket = bucket->next) {
                    475:                flushed_size += bucket->buflen;
                    476:        }
                    477: 
                    478:        if (flushed_size == 0) {
                    479:                /* Unlikely, but possible */
                    480:                return SUCCESS;
                    481:        }
                    482: 
                    483:        if (chain == &(stream->readfilters)) {
                    484:                /* Dump any newly flushed data to the read buffer */
                    485:                if (stream->readpos > 0) {
                    486:                        /* Back the buffer up */
                    487:                        memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos);
                    488:                        stream->readpos = 0;
                    489:                        stream->writepos -= stream->readpos;
                    490:                }
                    491:                if (flushed_size > (stream->readbuflen - stream->writepos)) {
                    492:                        /* Grow the buffer */
                    493:                        stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent);
                    494:                }
                    495:                while ((bucket = inp->head)) {
                    496:                        memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
                    497:                        stream->writepos += bucket->buflen;
                    498:                        php_stream_bucket_unlink(bucket TSRMLS_CC);
                    499:                        php_stream_bucket_delref(bucket TSRMLS_CC);
                    500:                }
                    501:        } else if (chain == &(stream->writefilters)) {
                    502:                /* Send flushed data to the stream */
                    503:                while ((bucket = inp->head)) {
                    504:                        stream->ops->write(stream, bucket->buf, bucket->buflen TSRMLS_CC);
                    505:                        php_stream_bucket_unlink(bucket TSRMLS_CC);
                    506:                        php_stream_bucket_delref(bucket TSRMLS_CC);
                    507:                }
                    508:        }
                    509: 
                    510:        return SUCCESS;
                    511: }
                    512: 
                    513: PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC)
                    514: {
                    515:        if (filter->prev) {
                    516:                filter->prev->next = filter->next;
                    517:        } else {
                    518:                filter->chain->head = filter->next;
                    519:        }
                    520:        if (filter->next) {
                    521:                filter->next->prev = filter->prev;
                    522:        } else {
                    523:                filter->chain->tail = filter->prev;
                    524:        }
                    525: 
                    526:        if (filter->rsrc_id > 0) {
                    527:                zend_list_delete(filter->rsrc_id);
                    528:        }
                    529: 
                    530:        if (call_dtor) {
                    531:                php_stream_filter_free(filter TSRMLS_CC);
                    532:                return NULL;
                    533:        }
                    534:        return filter;
                    535: }
                    536: 
                    537: /*
                    538:  * Local variables:
                    539:  * tab-width: 4
                    540:  * c-basic-offset: 4
                    541:  * End:
                    542:  * vim600: noet sw=4 ts=4 fdm=marker
                    543:  * vim<600: noet sw=4 ts=4
                    544:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>