[BACK]Return to termcap.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libterm

Annotation of src/lib/libterm/termcap.c, Revision 1.41

1.41    ! christos    1: /*     $NetBSD: termcap.c,v 1.40 2001/11/02 18:24:20 christos Exp $    */
1.6       cgd         2:
1.1       cgd         3: /*
1.6       cgd         4:  * Copyright (c) 1980, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  * 3. All advertising materials mentioning features or use of this software
                     16:  *    must display the following acknowledgement:
                     17:  *     This product includes software developed by the University of
                     18:  *     California, Berkeley and its contributors.
                     19:  * 4. Neither the name of the University nor the names of its contributors
                     20:  *    may be used to endorse or promote products derived from this software
                     21:  *    without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     33:  * SUCH DAMAGE.
                     34:  */
                     35:
1.12      lukem      36: #include <sys/cdefs.h>
1.1       cgd        37: #ifndef lint
1.6       cgd        38: #if 0
                     39: static char sccsid[] = "@(#)termcap.c  8.1 (Berkeley) 6/4/93";
                     40: #else
1.41    ! christos   41: __RCSID("$NetBSD: termcap.c,v 1.40 2001/11/02 18:24:20 christos Exp $");
1.6       cgd        42: #endif
1.1       cgd        43: #endif /* not lint */
                     44:
                     45:
1.38      christos   46: #include <sys/types.h>
                     47: #include <sys/param.h>
1.21      lukem      48: #include <assert.h>
                     49: #include <ctype.h>
1.4       cgd        50: #include <stdio.h>
                     51: #include <stdlib.h>
                     52: #include <string.h>
1.13      lukem      53: #include <termcap.h>
1.18      blymn      54: #include <errno.h>
1.1       cgd        55: #include "pathnames.h"
1.23      blymn      56: #include "termcap_private.h"
1.18      blymn      57:
1.38      christos   58: #define        PBUFSIZ         MAXPATHLEN      /* max length of filename path */
                     59: #define        PVECSIZ         32              /* max number of names in path */
                     60:
1.1       cgd        61: /*
                     62:  * termcap - routines for dealing with the terminal capability data base
                     63:  *
                     64:  * BUG:                Should use a "last" pointer in tbuf, so that searching
                     65:  *             for capabilities alphabetically would not be a n**2/2
                     66:  *             process when large numbers of capabilities are given.
                     67:  * Note:       If we add a last pointer now we will screw up the
                     68:  *             tc capability. We really should compile termcap.
                     69:  *
                     70:  * Essentially all the work here is scanning and decoding escapes
                     71:  * in string capabilities.  We don't use stdio because the editor
                     72:  * doesn't, and because living w/o it is not hard.
                     73:  */
                     74:
1.6       cgd        75: static char *tbuf;     /* termcap buffer */
1.18      blymn      76: static  struct tinfo *fbuf;     /* untruncated termcap buffer */
1.1       cgd        77:
                     78: /*
1.31      blymn      79:  * Set the termcap entry to the arbitrary string passed in, this can
                     80:  * be used to provide a "dummy" termcap entry if a real one does not
                     81:  * exist.  This function will malloc the buffer and space for the
                     82:  * string.  If an error occurs return -1 otherwise return 0.
                     83:  */
                     84: int
                     85: t_setinfo(struct tinfo **bp, const char *entry)
                     86: {
1.36      blymn      87:        char capability[256], *cap_ptr;
                     88:        size_t limit;
                     89:
1.37      lukem      90:        _DIAGASSERT(bp != NULL);
                     91:        _DIAGASSERT(entry != NULL);
                     92:
1.31      blymn      93:        if ((*bp = malloc(sizeof(struct tinfo))) == NULL)
                     94:                return -1;
                     95:
                     96:        if (((*bp)->info = (char *) malloc(strlen(entry) + 1)) == NULL)
                     97:                return -1;
                     98:
                     99:        strcpy((*bp)->info, entry);
1.36      blymn     100:
                    101:        cap_ptr = capability;
                    102:        limit = 255;
                    103:        (*bp)->up = t_getstr(*bp, "up", &cap_ptr, &limit);
1.39      christos  104:        if ((*bp)->up)
                    105:                (*bp)->up = strdup((*bp)->up);
1.36      blymn     106:        cap_ptr = capability;
                    107:        limit = 255;
                    108:        (*bp)->bc = t_getstr(*bp, "bc", &cap_ptr, &limit);
1.39      christos  109:        if ((*bp)->bc)
                    110:                (*bp)->bc = strdup((*bp)->bc);
1.40      christos  111:        (*bp)->tbuf = NULL;
1.36      blymn     112:
1.31      blymn     113:        return 0;
                    114: }
                    115:
                    116: /*
1.18      blymn     117:  * Get an extended entry for the terminal name.  This differs from
                    118:  * tgetent only in a) the buffer is malloc'ed for the caller and
                    119:  * b) the termcap entry is not truncated to 1023 characters.
1.1       cgd       120:  */
1.18      blymn     121:
1.4       cgd       122: int
1.18      blymn     123: t_getent(bp, name)
                    124:        struct tinfo **bp;
1.14      mycroft   125:        const char *name;
1.1       cgd       126: {
1.13      lukem     127:        char  *p;
                    128:        char  *cp;
1.6       cgd       129:        char **fname;
                    130:        char  *home;
1.27      blymn     131:        int    i, did_getset;
1.36      blymn     132:        size_t limit;
1.6       cgd       133:        char   pathbuf[PBUFSIZ];        /* holds raw path of filenames */
                    134:        char  *pathvec[PVECSIZ];        /* to point to names in pathbuf */
                    135:        char  *termpath;
1.36      blymn     136:        char  capability[256], *cap_ptr;
                    137:
1.1       cgd       138:
1.21      lukem     139:        _DIAGASSERT(bp != NULL);
                    140:        _DIAGASSERT(name != NULL);
                    141:
1.18      blymn     142:        if ((*bp = malloc(sizeof(struct tinfo))) == NULL) return 0;
                    143:
1.6       cgd       144:        fname = pathvec;
1.1       cgd       145:        p = pathbuf;
                    146:        cp = getenv("TERMCAP");
                    147:        /*
                    148:         * TERMCAP can have one of two things in it. It can be the
1.9       mikel     149:         * name of a file to use instead of
                    150:         * /usr/share/misc/termcap. In this case it better start with
                    151:         * a "/". Or it can be an entry to use so we don't have to
1.16      abs       152:         * read the file. In this case cgetset() withh crunch out the
                    153:         * newlines.  If TERMCAP does not hold a file name then a path
                    154:         * of names is searched instead.  The path is found in the
                    155:         * TERMPATH variable, or becomes _PATH_DEF ("$HOME/.termcap
                    156:         * /usr/share/misc/termcap") if no TERMPATH exists.
1.1       cgd       157:         */
                    158:        if (!cp || *cp != '/') {        /* no TERMCAP or it holds an entry */
1.7       pk        159:                if ((termpath = getenv("TERMPATH")) != NULL)
1.38      christos  160:                        (void)strlcpy(pathbuf, termpath, sizeof(pathbuf));
1.1       cgd       161:                else {
1.7       pk        162:                        if ((home = getenv("HOME")) != NULL) {
                    163:                                /* set up default */
1.1       cgd       164:                                p += strlen(home);      /* path, looking in */
1.38      christos  165:                                (void)strlcpy(pathbuf, home,
                    166:                                    sizeof(pathbuf)); /* $HOME first */
                    167:                                if ((p - pathbuf) < sizeof(pathbuf) - 1)
                    168:                                    *p++ = '/';
1.1       cgd       169:                        }       /* if no $HOME look in current directory */
1.38      christos  170:                        if ((p - pathbuf) < sizeof(pathbuf) - 1) {
                    171:                            (void)strlcpy(p, _PATH_DEF,
                    172:                                sizeof(pathbuf) - (p - pathbuf));
                    173:                        }
1.1       cgd       174:                }
                    175:        }
1.38      christos  176:        else {
                    177:                /* user-defined name in TERMCAP; still can be tokenized */
                    178:                (void)strlcpy(pathbuf, cp, sizeof(pathbuf));
                    179:        }
1.1       cgd       180:
                    181:        *fname++ = pathbuf;     /* tokenize path into vector of names */
                    182:        while (*++p)
                    183:                if (*p == ' ' || *p == ':') {
                    184:                        *p = '\0';
                    185:                        while (*++p)
                    186:                                if (*p != ' ' && *p != ':')
                    187:                                        break;
                    188:                        if (*p == '\0')
                    189:                                break;
                    190:                        *fname++ = p;
                    191:                        if (fname >= pathvec + PVECSIZ) {
                    192:                                fname--;
                    193:                                break;
                    194:                        }
                    195:                }
1.38      christos  196:        *fname = NULL;                  /* mark end of vector */
1.27      blymn     197:
1.28      christos  198:        /*
                    199:         * try ignoring TERMCAP if it has a ZZ in it, we do this
                    200:         * because a TERMCAP with ZZ in it indicates the entry has been
                    201:         * exported by another program using the "old" interface, the
                    202:         * termcap entry has been truncated and ZZ points to an address
                    203:         * in the exporting programs memory space which is of no use
                    204:         * here - anyone who is exporting the termcap entry and then
                    205:         * reading it back again in the same program deserves to be
                    206:         * taken out, beaten up, dragged about, shot and then hurt some
                    207:         * more.
                    208:         */
1.27      blymn     209:        did_getset = 0;
                    210:        if (cp && *cp && *cp != '/' && strstr(cp, ":ZZ") == NULL) {
                    211:                did_getset = 1;
1.4       cgd       212:                if (cgetset(cp) < 0)
1.6       cgd       213:                        return (-2);
1.27      blymn     214:        }
                    215:
1.11      mrg       216:        /*
                    217:         * XXX potential security hole here in a set-id program if the
                    218:         * user had setup name to be built from a path they can not
                    219:         * normally read.
                    220:         */
1.18      blymn     221:        (*bp)->info = NULL;
                    222:        i = cgetent(&((*bp)->info), pathvec, name);
1.8       jtc       223:
1.28      christos  224:        /*
                    225:         * if we get an error and we skipped doing the cgetset before
                    226:         * we try with TERMCAP in place - we may be using a truncated
                    227:         * termcap entry but what else can one do?
                    228:         */
1.27      blymn     229:        if ((i < 0) && (did_getset == 0)) {
                    230:                if (cp && *cp && *cp != '/')
                    231:                        if (cgetset(cp) < 0)
                    232:                                return (-2);
                    233:                i = cgetent(&((*bp)->info), pathvec, name);
                    234:        }
1.31      blymn     235:
1.6       cgd       236:        /* no tc reference loop return code in libterm XXX */
1.4       cgd       237:        if (i == -3)
1.6       cgd       238:                return (-1);
1.36      blymn     239:
                    240:          /* fill in t_goto capabilities - this prevents memory leaks
                    241:           * and is more efficient than fetching these capabilities
                    242:           * every time t_goto is called.
                    243:           */
                    244:        if (i >= 0) {
                    245:                cap_ptr = capability;
                    246:                limit = 255;
                    247:                (*bp)->up = t_getstr(*bp, "up", &cap_ptr, &limit);
1.39      christos  248:                if ((*bp)->up)
                    249:                        (*bp)->up = strdup((*bp)->up);
1.36      blymn     250:                cap_ptr = capability;
                    251:                limit = 255;
                    252:                (*bp)->bc = t_getstr(*bp, "bc", &cap_ptr, &limit);
1.39      christos  253:                if ((*bp)->bc)
                    254:                        (*bp)->bc = strdup((*bp)->bc);
1.40      christos  255:                (*bp)->tbuf = NULL;
1.36      blymn     256:        }
                    257:
1.6       cgd       258:        return (i + 1);
1.1       cgd       259: }
                    260:
                    261: /*
1.18      blymn     262:  * Get an entry for terminal name in buffer bp from the termcap file.
                    263:  */
                    264: int
                    265: tgetent(bp, name)
                    266:        char *bp;
                    267:        const char *name;
                    268: {
                    269:        int i, plen, elen, c;
                    270:         char *ptrbuf = NULL;
                    271:
                    272:        i = t_getent(&fbuf, name);
                    273:
                    274:        if (i == 1) {
1.28      christos  275:                /*
                    276:                 * stash the full buffer pointer as the ZZ capability
                    277:                 * in the termcap buffer passed.
                    278:                 */
1.18      blymn     279:                 plen = asprintf(&ptrbuf, ":ZZ=%p", fbuf->info);
1.38      christos  280:                (void)strlcpy(bp, fbuf->info, 1024);
1.18      blymn     281:                 elen = strlen(bp);
1.28      christos  282:                /*
                    283:                 * backup over the entry if the addition of the full
                    284:                 * buffer pointer will overflow the buffer passed.  We
                    285:                 * want to truncate the termcap entry on a capability
                    286:                 * boundary.
                    287:                 */
1.18      blymn     288:                 if ((elen + plen) > 1023) {
                    289:                        bp[1023 - plen] = '\0';
                    290:                        for (c = (elen - plen); c > 0; c--) {
                    291:                                if (bp[c] == ':') {
                    292:                                        bp[c] = '\0';
                    293:                                        break;
                    294:                                }
                    295:                        }
                    296:                }
                    297:
                    298:                strcat(bp, ptrbuf);
                    299:                 tbuf = bp;
                    300:        }
                    301:
                    302:        return i;
                    303: }
                    304:
                    305: /*
1.1       cgd       306:  * Return the (numeric) option id.
                    307:  * Numeric options look like
                    308:  *     li#80
                    309:  * i.e. the option string is separated from the numeric value by
                    310:  * a # character.  If the option is not found we return -1.
                    311:  * Note that we handle octal numbers beginning with 0.
                    312:  */
1.4       cgd       313: int
1.18      blymn     314: t_getnum(info, id)
                    315:        struct tinfo *info;
1.14      mycroft   316:        const char *id;
1.1       cgd       317: {
1.4       cgd       318:        long num;
1.1       cgd       319:
1.28      christos  320:        _DIAGASSERT(info != NULL);
1.21      lukem     321:        _DIAGASSERT(id != NULL);
                    322:
1.18      blymn     323:        if (cgetnum(info->info, id, &num) == 0)
1.15      agc       324:                return (int)(num);
1.4       cgd       325:        else
1.6       cgd       326:                return (-1);
1.1       cgd       327: }
                    328:
1.18      blymn     329: int
                    330: tgetnum(id)
                    331:        const char *id;
                    332: {
                    333:        return t_getnum(fbuf, id);
                    334: }
                    335:
1.1       cgd       336: /*
                    337:  * Handle a flag option.
                    338:  * Flag options are given "naked", i.e. followed by a : or the end
                    339:  * of the buffer.  Return 1 if we find the option, or 0 if it is
                    340:  * not given.
                    341:  */
1.18      blymn     342: int t_getflag(info, id)
                    343:        struct tinfo *info;
                    344:        const char *id;
                    345: {
1.28      christos  346:        _DIAGASSERT(info != NULL);
                    347:        _DIAGASSERT(id != NULL);
                    348:
1.18      blymn     349:        return (cgetcap(info->info, id, ':') != NULL);
                    350: }
                    351:
1.6       cgd       352: int
1.1       cgd       353: tgetflag(id)
1.14      mycroft   354:        const char *id;
1.1       cgd       355: {
1.18      blymn     356:        return t_getflag(fbuf, id);
1.1       cgd       357: }
                    358:
                    359: /*
                    360:  * Get a string valued option.
                    361:  * These are given as
                    362:  *     cl=^Z
                    363:  * Much decoding is done on the strings, and the strings are
                    364:  * placed in area, which is a ref parameter which is updated.
1.18      blymn     365:  * limit is the number of characters allowed to be put into
                    366:  * area, this is updated.
1.1       cgd       367:  */
                    368: char *
1.18      blymn     369: t_getstr(info, id, area, limit)
                    370:        struct tinfo *info;
1.14      mycroft   371:        const char *id;
                    372:        char **area;
1.19      blymn     373:        size_t *limit;
1.1       cgd       374: {
1.4       cgd       375:        char *s;
1.1       cgd       376:        int i;
1.21      lukem     377:
1.28      christos  378:        _DIAGASSERT(info != NULL);
1.21      lukem     379:        _DIAGASSERT(id != NULL);
1.29      lukem     380:        /* area may be NULL */
1.17      simonb    381:
1.4       cgd       382:
1.28      christos  383:        if ((i = cgetstr(info->info, id, &s)) < 0) {
1.18      blymn     384:                errno = ENOENT;
1.26      blymn     385:                if ((area == NULL) && (limit != NULL))
1.25      blymn     386:                        *limit = 0;
1.18      blymn     387:                return NULL;
                    388:        }
                    389:
1.20      blymn     390:        if (area != NULL) {
1.28      christos  391:                /*
                    392:                 * check if there is room for the new entry to be put into
                    393:                 * area
                    394:                 */
1.20      blymn     395:                if (limit != NULL && (*limit < i)) {
1.35      thorpej   396:                        errno = E2BIG;
1.34      christos  397:                        free(s);
1.20      blymn     398:                        return NULL;
                    399:                }
1.35      thorpej   400:
1.39      christos  401:                (void)strcpy(*area, s);
                    402:                free(s);
                    403:                s = *area;
1.20      blymn     404:                *area += i + 1;
                    405:                if (limit != NULL) *limit -= i;
1.35      thorpej   406:
                    407:                return (s);
1.20      blymn     408:        } else {
1.28      christos  409:                _DIAGASSERT(limit != NULL);
1.20      blymn     410:                *limit = i;
1.33      itojun    411:                free(s);
1.4       cgd       412:                return NULL;
1.18      blymn     413:        }
                    414: }
                    415:
                    416: /*
                    417:  * Get a string valued option.
                    418:  * These are given as
                    419:  *     cl=^Z
                    420:  * Much decoding is done on the strings, and the strings are
                    421:  * placed in area, which is a ref parameter which is updated.
                    422:  * No checking on area overflow.
                    423:  */
                    424: char *
                    425: tgetstr(id, area)
                    426:        const char *id;
                    427:        char **area;
                    428: {
                    429:        struct tinfo dummy;
1.28      christos  430:        char ids[3];
                    431:
                    432:        _DIAGASSERT(id != NULL);
                    433:        /*
                    434:         * XXX
                    435:         * This is for all the boneheaded programs that relied on tgetstr
                    436:         * to look only at the first 2 characters of the string passed...
                    437:         */
                    438:        ids[0] = id[0];
                    439:        ids[1] = id[1];
                    440:        ids[2] = '\0';
1.18      blymn     441:
                    442:        if ((id[0] == 'Z') && (id[1] == 'Z')) {
                    443:                dummy.info = tbuf;
1.28      christos  444:                return t_getstr(&dummy, ids, area, NULL);
1.18      blymn     445:        }
                    446:        else
1.28      christos  447:                return t_getstr(fbuf, ids, area, NULL);
1.18      blymn     448: }
                    449:
1.30      blymn     450: /*
1.41    ! christos  451:  * Return a string valued option specified by id, allocating memory to
        !           452:  * an internal buffer as necessary. The memory allocated can be
        !           453:  * free'd by a call to t_freent().
1.39      christos  454:  *
1.41    ! christos  455:  * If the string is not found or memory allocation fails then NULL
        !           456:  * is returned.
1.30      blymn     457:  */
1.40      christos  458: #define BSIZE 256
1.30      blymn     459: char *
1.40      christos  460: t_agetstr(struct tinfo *info, const char *id)
1.30      blymn     461: {
1.39      christos  462:        size_t new_size;
1.40      christos  463:        struct tbuf *tb;
1.30      blymn     464:
                    465:        _DIAGASSERT(info != NULL);
                    466:        _DIAGASSERT(id != NULL);
                    467:
                    468:        t_getstr(info, id, NULL, &new_size);
                    469:
1.40      christos  470:        /* either the string is empty or the capability does not exist. */
                    471:        if (new_size == 0)
1.30      blymn     472:                return NULL;
                    473:
1.40      christos  474:        if ((tb = info->tbuf) == NULL || (tb->eptr - tb->ptr) < new_size) {
                    475:                if (new_size < BSIZE)
                    476:                        new_size = BSIZE;
                    477:                else
                    478:                        new_size++;
                    479:
                    480:                if ((tb = malloc(sizeof(*info->tbuf))) == NULL)
                    481:                        return NULL;
                    482:
                    483:                if ((tb->data = tb->ptr = tb->eptr = malloc(new_size)) == NULL)
1.30      blymn     484:                        return NULL;
1.40      christos  485:
                    486:                tb->eptr += new_size;
                    487:
                    488:                if (info->tbuf != NULL)
                    489:                        tb->next = info->tbuf;
                    490:                else
                    491:                        tb->next = NULL;
                    492:
                    493:                info->tbuf = tb;
1.30      blymn     494:        }
1.40      christos  495:        return t_getstr(info, id, &tb->ptr, NULL);
1.30      blymn     496: }
                    497:
1.18      blymn     498: /*
                    499:  * Free the buffer allocated by t_getent
                    500:  *
                    501:  */
                    502: void
                    503: t_freent(info)
                    504:        struct tinfo *info;
                    505: {
1.40      christos  506:        struct tbuf *tb;
1.28      christos  507:        _DIAGASSERT(info != NULL);
1.18      blymn     508:        free(info->info);
1.36      blymn     509:        if (info->up != NULL)
                    510:                free(info->up);
                    511:        if (info->bc != NULL)
                    512:                free(info->bc);
1.40      christos  513:        for (tb = info->tbuf; tb;) {
                    514:                tb = info->tbuf->next;
                    515:                free(info->tbuf->data);
                    516:                free(info->tbuf);
                    517:        }
1.18      blymn     518:        free(info);
1.1       cgd       519: }
1.24      blymn     520:
                    521: /*
                    522:  * Get the terminal name string from the termcap entry.
                    523:  *
                    524:  */
                    525: int
1.28      christos  526: t_getterm(info, area, limit)
                    527:        struct tinfo *info;
                    528:        char **area;
                    529:        size_t *limit;
1.24      blymn     530: {
                    531:        char *endp;
                    532:        size_t count;
                    533:
1.28      christos  534:        _DIAGASSERT(info != NULL);
                    535:        if ((endp = strchr(info->info, ':')) == NULL) {
1.24      blymn     536:                errno = EINVAL;
                    537:                return -1;
                    538:        }
                    539:
                    540:
                    541:        count = endp - info->info + 1;
                    542:        if (area == NULL) {
1.28      christos  543:                _DIAGASSERT(limit != NULL);
1.24      blymn     544:                *limit = count;
                    545:                return 0;
                    546:        } else {
                    547:                if ((limit != NULL) && (count > *limit)) {
                    548:                        errno = E2BIG;
                    549:                        return -1;
                    550:                }
                    551:
1.38      christos  552:                (void)strlcpy(*area, info->info, count);
1.24      blymn     553:                if (limit != NULL)
                    554:                        *limit -= count;
                    555:        }
                    556:
                    557:        return 0;
                    558: }

CVSweb <webmaster@jp.NetBSD.org>