Annotation of embedaddon/php/ext/zip/lib/zip_open.c, revision 1.1

1.1     ! misho       1: /*
        !             2:   zip_open.c -- open zip archive
        !             3:   Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
        !             4: 
        !             5:   This file is part of libzip, a library to manipulate ZIP archives.
        !             6:   The authors can be contacted at <libzip@nih.at>
        !             7: 
        !             8:   Redistribution and use in source and binary forms, with or without
        !             9:   modification, are permitted provided that the following conditions
        !            10:   are met:
        !            11:   1. Redistributions of source code must retain the above copyright
        !            12:      notice, this list of conditions and the following disclaimer.
        !            13:   2. Redistributions in binary form must reproduce the above copyright
        !            14:      notice, this list of conditions and the following disclaimer in
        !            15:      the documentation and/or other materials provided with the
        !            16:      distribution.
        !            17:   3. The names of the authors may not be used to endorse or promote
        !            18:      products derived from this software without specific prior
        !            19:      written permission.
        !            20:  
        !            21:   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
        !            22:   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        !            23:   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            24:   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
        !            25:   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            26:   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
        !            27:   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        !            28:   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
        !            29:   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
        !            30:   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
        !            31:   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            32: */
        !            33: 
        !            34: 
        !            35: 
        !            36: #include <sys/stat.h>
        !            37: #include <errno.h>
        !            38: #include <limits.h>
        !            39: #include <stdio.h>
        !            40: #include <stdlib.h>
        !            41: #include <string.h>
        !            42: 
        !            43: #include "zipint.h"
        !            44: 
        !            45: static void set_error(int *, struct zip_error *, int);
        !            46: static struct zip *_zip_allocate_new(const char *, int *);
        !            47: static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *);
        !            48: static void _zip_check_torrentzip(struct zip *);
        !            49: static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t);
        !            50: static int _zip_file_exists(const char *, int, int *);
        !            51: static int _zip_headercomp(struct zip_dirent *, int,
        !            52:                           struct zip_dirent *, int);
        !            53: static unsigned char *_zip_memmem(const unsigned char *, int,
        !            54:                                  const unsigned char *, int);
        !            55: static struct zip_cdir *_zip_readcdir(FILE *, unsigned char *, unsigned char *,
        !            56:                                 int, int, struct zip_error *);
        !            57: 
        !            58: 
        !            59: 
        !            60: ZIP_EXTERN(struct zip *)
        !            61: zip_open(const char *fn, int flags, int *zep)
        !            62: {
        !            63:     FILE *fp;
        !            64:     struct zip *za;
        !            65:     struct zip_cdir *cdir;
        !            66:     int i;
        !            67:     off_t len;
        !            68: 
        !            69:     if (flags & ZIP_OVERWRITE) {
        !            70:        return _zip_allocate_new(fn, zep);
        !            71:     }
        !            72: 
        !            73:     switch (_zip_file_exists(fn, flags, zep)) {
        !            74:     case -1:
        !            75:                        if (!(flags & ZIP_OVERWRITE)) {
        !            76:                                return NULL;
        !            77:                        }
        !            78: 
        !            79:     case 0:
        !            80:        return _zip_allocate_new(fn, zep);
        !            81: 
        !            82:     default:
        !            83:        break;
        !            84:     }
        !            85: 
        !            86:     if ((fp=fopen(fn, "rb")) == NULL) {
        !            87:        set_error(zep, NULL, ZIP_ER_OPEN);
        !            88:        return NULL;
        !            89:     }
        !            90: 
        !            91:     fseeko(fp, 0, SEEK_END);
        !            92:     len = ftello(fp);
        !            93: 
        !            94:     /* treat empty files as empty archives */
        !            95:     if (len == 0) {
        !            96:        if ((za=_zip_allocate_new(fn, zep)) == NULL)
        !            97:            fclose(fp);
        !            98:        else
        !            99:            za->zp = fp;
        !           100:        return za;
        !           101:     }
        !           102: 
        !           103:     cdir = _zip_find_central_dir(fp, flags, zep, len);
        !           104:     if (cdir == NULL) {
        !           105:        fclose(fp);
        !           106:        return NULL;
        !           107:     }
        !           108: 
        !           109:     if ((za=_zip_allocate_new(fn, zep)) == NULL) {
        !           110:        _zip_cdir_free(cdir);
        !           111:        fclose(fp);
        !           112:        return NULL;
        !           113:     }
        !           114: 
        !           115:     za->cdir = cdir;
        !           116:     za->zp = fp;
        !           117: 
        !           118:     if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry))
        !           119:                                              * cdir->nentry)) == NULL) {
        !           120:        set_error(zep, NULL, ZIP_ER_MEMORY);
        !           121:        _zip_free(za);
        !           122:        return NULL;
        !           123:     }
        !           124:     for (i=0; i<cdir->nentry; i++)
        !           125:        _zip_entry_new(za);
        !           126: 
        !           127:     _zip_check_torrentzip(za);
        !           128:     za->ch_flags = za->flags;
        !           129: 
        !           130:     return za;
        !           131: }
        !           132: 
        !           133: 
        !           134: 
        !           135: static void
        !           136: set_error(int *zep, struct zip_error *err, int ze)
        !           137: {
        !           138:     int se;
        !           139: 
        !           140:     if (err) {
        !           141:        _zip_error_get(err, &ze, &se);
        !           142:        if (zip_error_get_sys_type(ze) == ZIP_ET_SYS)
        !           143:            errno = se;
        !           144:     }
        !           145: 
        !           146:     if (zep)
        !           147:        *zep = ze;
        !           148: }
        !           149: 
        !           150: 
        !           151: 
        !           152: /* _zip_readcdir:
        !           153:    tries to find a valid end-of-central-directory at the beginning of
        !           154:    buf, and then the corresponding central directory entries.
        !           155:    Returns a struct zip_cdir which contains the central directory 
        !           156:    entries, or NULL if unsuccessful. */
        !           157: 
        !           158: static struct zip_cdir *
        !           159: _zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen,
        !           160:              int flags, struct zip_error *error)
        !           161: {
        !           162:     struct zip_cdir *cd;
        !           163:     unsigned char *cdp, **bufp;
        !           164:     int i, comlen, nentry;
        !           165:     unsigned int left;
        !           166: 
        !           167:     comlen = buf + buflen - eocd - EOCDLEN;
        !           168:     if (comlen < 0) {
        !           169:        /* not enough bytes left for comment */
        !           170:        _zip_error_set(error, ZIP_ER_NOZIP, 0);
        !           171:        return NULL;
        !           172:     }
        !           173: 
        !           174:     /* check for end-of-central-dir magic */
        !           175:     if (memcmp(eocd, EOCD_MAGIC, 4) != 0) {
        !           176:        _zip_error_set(error, ZIP_ER_NOZIP, 0);
        !           177:        return NULL;
        !           178:     }
        !           179: 
        !           180:     if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) {
        !           181:        _zip_error_set(error, ZIP_ER_MULTIDISK, 0);
        !           182:        return NULL;
        !           183:     }
        !           184: 
        !           185:     cdp = eocd + 8;
        !           186:     /* number of cdir-entries on this disk */
        !           187:     i = _zip_read2(&cdp);
        !           188:     /* number of cdir-entries */
        !           189:     nentry = _zip_read2(&cdp);
        !           190: 
        !           191:     if ((cd=_zip_cdir_new(nentry, error)) == NULL)
        !           192:        return NULL;
        !           193: 
        !           194:     cd->size = _zip_read4(&cdp);
        !           195:     cd->offset = _zip_read4(&cdp);
        !           196:     cd->comment = NULL;
        !           197:     cd->comment_len = _zip_read2(&cdp);
        !           198: 
        !           199:     if ((comlen < cd->comment_len) || (cd->nentry != i)) {
        !           200:        _zip_error_set(error, ZIP_ER_NOZIP, 0);
        !           201:        free(cd);
        !           202:        return NULL;
        !           203:     }
        !           204:     if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) {
        !           205:        _zip_error_set(error, ZIP_ER_INCONS, 0);
        !           206:        free(cd);
        !           207:        return NULL;
        !           208:     }
        !           209: 
        !           210:     if (cd->comment_len) {
        !           211:        if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN,
        !           212:                                             cd->comment_len, error))
        !           213:            == NULL) {
        !           214:            free(cd);
        !           215:            return NULL;
        !           216:        }
        !           217:     }
        !           218: 
        !           219:     if (cd->size < (unsigned int)(eocd-buf)) {
        !           220:        /* if buffer already read in, use it */
        !           221:        cdp = eocd - cd->size;
        !           222:        bufp = &cdp;
        !           223:     }
        !           224:     else {
        !           225:        /* go to start of cdir and read it entry by entry */
        !           226:        bufp = NULL;
        !           227:        clearerr(fp);
        !           228:        fseeko(fp, cd->offset, SEEK_SET);
        !           229:        /* possible consistency check: cd->offset =
        !           230:           len-(cd->size+cd->comment_len+EOCDLEN) ? */
        !           231:        if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) {
        !           232:            /* seek error or offset of cdir wrong */
        !           233:            if (ferror(fp))
        !           234:                _zip_error_set(error, ZIP_ER_SEEK, errno);
        !           235:            else
        !           236:                _zip_error_set(error, ZIP_ER_NOZIP, 0);
        !           237:            free(cd);
        !           238:            return NULL;
        !           239:        }
        !           240:     }
        !           241: 
        !           242:     left = cd->size;
        !           243:     i=0;
        !           244:     do {
        !           245:        if (i == cd->nentry && left > 0) {
        !           246:            /* Infozip extension for more than 64k entries:
        !           247:               nentries wraps around, size indicates correct EOCD */
        !           248:            _zip_cdir_grow(cd, cd->nentry+0x10000, error);
        !           249:        }
        !           250: 
        !           251:        if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) {
        !           252:            cd->nentry = i;
        !           253:            _zip_cdir_free(cd);
        !           254:            return NULL;
        !           255:        }
        !           256:        i++;
        !           257:        
        !           258:     } while (i<cd->nentry);
        !           259:     
        !           260:     return cd;
        !           261: }
        !           262: 
        !           263: 
        !           264: 
        !           265: /* _zip_checkcons:
        !           266:    Checks the consistency of the central directory by comparing central
        !           267:    directory entries with local headers and checking for plausible
        !           268:    file and header offsets. Returns -1 if not plausible, else the
        !           269:    difference between the lowest and the highest fileposition reached */
        !           270: 
        !           271: static int
        !           272: _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error)
        !           273: {
        !           274:     int i;
        !           275:     unsigned int min, max, j;
        !           276:     struct zip_dirent temp;
        !           277: 
        !           278:     if (cd->nentry) {
        !           279:        max = cd->entry[0].offset;
        !           280:        min = cd->entry[0].offset;
        !           281:     }
        !           282:     else
        !           283:        min = max = 0;
        !           284: 
        !           285:     for (i=0; i<cd->nentry; i++) {
        !           286:        if (cd->entry[i].offset < min)
        !           287:            min = cd->entry[i].offset;
        !           288:        if (min > cd->offset) {
        !           289:            _zip_error_set(error, ZIP_ER_NOZIP, 0);
        !           290:            return -1;
        !           291:        }
        !           292:        
        !           293:        j = cd->entry[i].offset + cd->entry[i].comp_size
        !           294:            + cd->entry[i].filename_len + LENTRYSIZE;
        !           295:        if (j > max)
        !           296:            max = j;
        !           297:        if (max > cd->offset) {
        !           298:            _zip_error_set(error, ZIP_ER_NOZIP, 0);
        !           299:            return -1;
        !           300:        }
        !           301:        
        !           302:        if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) {
        !           303:            _zip_error_set(error, ZIP_ER_SEEK, 0);
        !           304:            return -1;
        !           305:        }
        !           306:        
        !           307:        if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1)
        !           308:            return -1;
        !           309:        
        !           310:        if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) {
        !           311:            _zip_error_set(error, ZIP_ER_INCONS, 0);
        !           312:            _zip_dirent_finalize(&temp);
        !           313:            return -1;
        !           314:        }
        !           315:        _zip_dirent_finalize(&temp);
        !           316:     }
        !           317: 
        !           318:     return max - min;
        !           319: }
        !           320: 
        !           321: 
        !           322: 
        !           323: /* _zip_check_torrentzip:
        !           324:    check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
        !           325: 
        !           326: static void
        !           327: _zip_check_torrentzip(struct zip *za)
        !           328: {
        !           329:     uLong crc_got, crc_should;
        !           330:     char buf[8+1];
        !           331:     char *end;
        !           332: 
        !           333:     if (za->zp == NULL || za->cdir == NULL)
        !           334:        return;
        !           335: 
        !           336:     if (za->cdir->comment_len != TORRENT_SIG_LEN+8
        !           337:        || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0)
        !           338:        return;
        !           339:     
        !           340:     memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8);
        !           341:     buf[8] = '\0';
        !           342:     errno = 0;
        !           343:     crc_should = strtoul(buf, &end, 16);
        !           344:     if ((crc_should == UINT_MAX && errno != 0) || (end && *end))
        !           345:        return;
        !           346:     
        !           347:     if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size,
        !           348:                           &crc_got, NULL) < 0)
        !           349:            return;
        !           350: 
        !           351:     if (crc_got == crc_should)
        !           352:        za->flags |= ZIP_AFL_TORRENT;
        !           353: }
        !           354: 
        !           355: 
        !           356: 
        !           357: 
        !           358: /* _zip_headercomp:
        !           359:    compares two headers h1 and h2; if they are local headers, set
        !           360:    local1p or local2p respectively to 1, else 0. Return 0 if they
        !           361:    are identical, -1 if not. */
        !           362: 
        !           363: static int
        !           364: _zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2,
        !           365:           int local2p)
        !           366: {
        !           367:     if ((h1->version_needed != h2->version_needed)
        !           368: #if 0
        !           369:        /* some zip-files have different values in local
        !           370:           and global headers for the bitflags */
        !           371:        || (h1->bitflags != h2->bitflags)
        !           372: #endif
        !           373:        || (h1->comp_method != h2->comp_method)
        !           374:        || (h1->last_mod != h2->last_mod)
        !           375:        || (h1->filename_len != h2->filename_len)
        !           376:        || !h1->filename || !h2->filename
        !           377:        || strcmp(h1->filename, h2->filename))
        !           378:        return -1;
        !           379: 
        !           380:     /* check that CRC and sizes are zero if data descriptor is used */
        !           381:     if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p
        !           382:        && (h1->crc != 0
        !           383:            || h1->comp_size != 0
        !           384:            || h1->uncomp_size != 0))
        !           385:        return -1;
        !           386:     if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p
        !           387:        && (h2->crc != 0
        !           388:            || h2->comp_size != 0
        !           389:            || h2->uncomp_size != 0))
        !           390:        return -1;
        !           391:     
        !           392:     /* check that CRC and sizes are equal if no data descriptor is used */
        !           393:     if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0)
        !           394:        && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) {
        !           395:        if ((h1->crc != h2->crc)
        !           396:            || (h1->comp_size != h2->comp_size)
        !           397:            || (h1->uncomp_size != h2->uncomp_size))
        !           398:            return -1;
        !           399:     }
        !           400:     
        !           401:     if ((local1p == local2p)
        !           402:        && ((h1->extrafield_len != h2->extrafield_len)
        !           403:            || (h1->extrafield_len && h2->extrafield
        !           404:                && memcmp(h1->extrafield, h2->extrafield,
        !           405:                          h1->extrafield_len))))
        !           406:        return -1;
        !           407: 
        !           408:     /* if either is local, nothing more to check */
        !           409:     if (local1p || local2p)
        !           410:        return 0;
        !           411: 
        !           412:     if ((h1->version_madeby != h2->version_madeby)
        !           413:        || (h1->disk_number != h2->disk_number)
        !           414:        || (h1->int_attrib != h2->int_attrib)
        !           415:        || (h1->ext_attrib != h2->ext_attrib)
        !           416:        || (h1->offset != h2->offset)
        !           417:        || (h1->comment_len != h2->comment_len)
        !           418:        || (h1->comment_len && h2->comment
        !           419:            && memcmp(h1->comment, h2->comment, h1->comment_len)))
        !           420:        return -1;
        !           421: 
        !           422:     return 0;
        !           423: }
        !           424: 
        !           425: 
        !           426: 
        !           427: static struct zip *
        !           428: _zip_allocate_new(const char *fn, int *zep)
        !           429: {
        !           430:     struct zip *za;
        !           431:     struct zip_error error;
        !           432: 
        !           433:     if ((za=_zip_new(&error)) == NULL) {
        !           434:        set_error(zep, &error, 0);
        !           435:        return NULL;
        !           436:     }
        !           437:        
        !           438:     za->zn = strdup(fn);
        !           439:     if (!za->zn) {
        !           440:        _zip_free(za);
        !           441:        set_error(zep, NULL, ZIP_ER_MEMORY);
        !           442:        return NULL;
        !           443:     }
        !           444:     return za;
        !           445: }
        !           446: 
        !           447: 
        !           448: 
        !           449: static int
        !           450: _zip_file_exists(const char *fn, int flags, int *zep)
        !           451: {
        !           452:     struct stat st;
        !           453: 
        !           454:     if (fn == NULL) {
        !           455:        set_error(zep, NULL, ZIP_ER_INVAL);
        !           456:        return -1;
        !           457:     }
        !           458:     
        !           459:     if (stat(fn, &st) != 0) {
        !           460:        if (flags & ZIP_CREATE || flags & ZIP_OVERWRITE)
        !           461:            return 0;
        !           462:        else {
        !           463:            set_error(zep, NULL, ZIP_ER_OPEN);
        !           464:            return -1;
        !           465:        }
        !           466:     }
        !           467:     else if ((flags & ZIP_EXCL)) {
        !           468:        set_error(zep, NULL, ZIP_ER_EXISTS);
        !           469:        return -1;
        !           470:     }
        !           471:     /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL,
        !           472:        just like open() */
        !           473: 
        !           474:     return 1;
        !           475: }
        !           476: 
        !           477: 
        !           478: 
        !           479: static struct zip_cdir *
        !           480: _zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len)
        !           481: {
        !           482:     struct zip_cdir *cdir, *cdirnew;
        !           483:     unsigned char *buf, *match;
        !           484:     int a, best, buflen, i;
        !           485:     struct zip_error zerr;
        !           486: 
        !           487:     i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END);
        !           488:     if (i == -1 && errno != EFBIG) {
        !           489:        /* seek before start of file on my machine */
        !           490:        set_error(zep, NULL, ZIP_ER_SEEK);
        !           491:        return NULL;
        !           492:     }
        !           493: 
        !           494:     /* 64k is too much for stack */
        !           495:     if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) {
        !           496:        set_error(zep, NULL, ZIP_ER_MEMORY);
        !           497:        return NULL;
        !           498:     }
        !           499: 
        !           500:     clearerr(fp);
        !           501:     buflen = fread(buf, 1, CDBUFSIZE, fp);
        !           502: 
        !           503:     if (ferror(fp)) {
        !           504:        set_error(zep, NULL, ZIP_ER_READ);
        !           505:        free(buf);
        !           506:        return NULL;
        !           507:     }
        !           508:     
        !           509:     best = -1;
        !           510:     cdir = NULL;
        !           511:     match = buf;
        !           512:     _zip_error_set(&zerr, ZIP_ER_NOZIP, 0);
        !           513: 
        !           514:     while ((match=_zip_memmem(match, buflen-(match-buf)-18,
        !           515:                              (const unsigned char *)EOCD_MAGIC, 4))!=NULL) {
        !           516:        /* found match -- check, if good */
        !           517:        /* to avoid finding the same match all over again */
        !           518:        match++;
        !           519:        if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags,
        !           520:                                   &zerr)) == NULL)
        !           521:            continue;
        !           522: 
        !           523:        if (cdir) {
        !           524:            if (best <= 0)
        !           525:                best = _zip_checkcons(fp, cdir, &zerr);
        !           526:            a = _zip_checkcons(fp, cdirnew, &zerr);
        !           527:            if (best < a) {
        !           528:                _zip_cdir_free(cdir);
        !           529:                cdir = cdirnew;
        !           530:                best = a;
        !           531:            }
        !           532:            else
        !           533:                _zip_cdir_free(cdirnew);
        !           534:        }
        !           535:        else {
        !           536:            cdir = cdirnew;
        !           537:            if (flags & ZIP_CHECKCONS)
        !           538:                best = _zip_checkcons(fp, cdir, &zerr);
        !           539:            else
        !           540:                best = 0;
        !           541:        }
        !           542:        cdirnew = NULL;
        !           543:     }
        !           544: 
        !           545:     free(buf);
        !           546:     
        !           547:     if (best < 0) {
        !           548:        set_error(zep, &zerr, 0);
        !           549:        _zip_cdir_free(cdir);
        !           550:        return NULL;
        !           551:     }
        !           552: 
        !           553:     return cdir;
        !           554: }
        !           555: 
        !           556: 
        !           557: 
        !           558: static unsigned char *
        !           559: _zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, 
        !           560:        int littlelen)
        !           561: {
        !           562:     const unsigned char *p;
        !           563:     
        !           564:     if ((biglen < littlelen) || (littlelen == 0))
        !           565:        return NULL;
        !           566:     p = big-1;
        !           567:     while ((p=(const unsigned char *)
        !           568:                memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1)))
        !           569:           != NULL) {
        !           570:        if (memcmp(p+1, little+1, littlelen-1)==0)
        !           571:            return (unsigned char *)p;
        !           572:     }
        !           573: 
        !           574:     return NULL;
        !           575: }

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