home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / lib / libc / gen / getcap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-18  |  22.7 KB  |  1,039 lines

  1. /*-
  2.  * Copyright (c) 1992 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Casey Leedom of Lawrence Livermore National Laboratory.
  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 the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #if defined(LIBC_SCCS) && !defined(lint)
  38. static char sccsid[] = "@(#)getcap.c    5.15 (Berkeley) 3/19/93";
  39. #endif /* LIBC_SCCS and not lint */
  40.  
  41. #include <sys/types.h>
  42.  
  43. #include <ctype.h>
  44. #include <db.h>
  45. #include <errno.h>    
  46. #include <fcntl.h>
  47. #include <limits.h>
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include <unistd.h>
  52.  
  53. #define    BFRAG        1024
  54. #define    BSIZE        1024
  55. #define    ESC        ('[' & 037)    /* ASCII ESC */
  56. #define    MAX_RECURSION    32        /* maximum getent recursion */
  57. #define    SFRAG        100        /* cgetstr mallocs in SFRAG chunks */
  58.  
  59. #define RECOK    (char)0
  60. #define TCERR    (char)1
  61. #define    SHADOW    (char)2
  62.  
  63. static size_t     topreclen;    /* toprec length */
  64. static char    *toprec;    /* Additional record specified by cgetset() */
  65. static int     gottoprec;    /* Flag indicating retrieval of toprecord */
  66.  
  67. static int    cdbget __P((DB *, char **, char *));
  68. static int     getent __P((char **, u_int *, char **, int, char *, int, char *));
  69. static int    nfcmp __P((char *, char *));
  70.  
  71. /*
  72.  * Cgetset() allows the addition of a user specified buffer to be added
  73.  * to the database array, in effect "pushing" the buffer on top of the
  74.  * virtual database. 0 is returned on success, -1 on failure.
  75.  */
  76. int
  77. cgetset(ent)
  78.     char *ent;
  79. {
  80.     if (ent == NULL) {
  81.         if (toprec)
  82.             free(toprec);
  83.                 toprec = NULL;
  84.                 topreclen = 0;
  85.                 return (0);
  86.         }
  87.         topreclen = strlen(ent);
  88.         if ((toprec = malloc (topreclen + 1)) == NULL) {
  89.         errno = ENOMEM;
  90.                 return (-1);
  91.     }
  92.     gottoprec = 0;
  93.         (void)strcpy(toprec, ent);
  94.         return (0);
  95. }
  96.  
  97. /*
  98.  * Cgetcap searches the capability record buf for the capability cap with
  99.  * type `type'.  A pointer to the value of cap is returned on success, NULL
  100.  * if the requested capability couldn't be found.
  101.  *
  102.  * Specifying a type of ':' means that nothing should follow cap (:cap:).
  103.  * In this case a pointer to the terminating ':' or NUL will be returned if
  104.  * cap is found.
  105.  *
  106.  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
  107.  * return NULL.
  108.  */
  109. char *
  110. cgetcap(buf, cap, type)
  111.     char *buf, *cap;
  112.     int type;
  113. {
  114.     register char *bp, *cp;
  115.  
  116.     bp = buf;
  117.     for (;;) {
  118.         /*
  119.          * Skip past the current capability field - it's either the
  120.          * name field if this is the first time through the loop, or
  121.          * the remainder of a field whose name failed to match cap.
  122.          */
  123.         for (;;)
  124.             if (*bp == '\0')
  125.                 return (NULL);
  126.             else
  127.                 if (*bp++ == ':')
  128.                     break;
  129.  
  130.         /*
  131.          * Try to match (cap, type) in buf.
  132.          */
  133.         for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
  134.             continue;
  135.         if (*cp != '\0')
  136.             continue;
  137.         if (*bp == '@')
  138.             return (NULL);
  139.         if (type == ':') {
  140.             if (*bp != '\0' && *bp != ':')
  141.                 continue;
  142.             return(bp);
  143.         }
  144.         if (*bp != type)
  145.             continue;
  146.         bp++;
  147.         return (*bp == '@' ? NULL : bp);
  148.     }
  149.     /* NOTREACHED */
  150. }
  151.  
  152. /*
  153.  * Cgetent extracts the capability record name from the NULL terminated file
  154.  * array db_array and returns a pointer to a malloc'd copy of it in buf.
  155.  * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
  156.  * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
  157.  * -1 if the requested record couldn't be found, -2 if a system error was
  158.  * encountered (couldn't open/read a file, etc.), and -3 if a potential
  159.  * reference loop is detected.
  160.  */
  161. int
  162. cgetent(buf, db_array, name)
  163.     char **buf, **db_array, *name;
  164. {
  165.     u_int dummy;
  166.  
  167.     return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
  168. }
  169.  
  170. /*
  171.  * Getent implements the functions of cgetent.  If fd is non-negative,
  172.  * *db_array has already been opened and fd is the open file descriptor.  We
  173.  * do this to save time and avoid using up file descriptors for tc=
  174.  * recursions.
  175.  *
  176.  * Getent returns the same success/failure codes as cgetent.  On success, a
  177.  * pointer to a malloc'ed capability record with all tc= capabilities fully
  178.  * expanded and its length (not including trailing ASCII NUL) are left in
  179.  * *cap and *len.
  180.  *
  181.  * Basic algorithm:
  182.  *    + Allocate memory incrementally as needed in chunks of size BFRAG
  183.  *      for capability buffer.
  184.  *    + Recurse for each tc=name and interpolate result.  Stop when all
  185.  *      names interpolated, a name can't be found, or depth exceeds
  186.  *      MAX_RECURSION.
  187.  */
  188. static int
  189. getent(cap, len, db_array, fd, name, depth, nfield)
  190.     char **cap, **db_array, *name, *nfield;
  191.     u_int *len;
  192.     int fd, depth;
  193. {
  194.     DB *capdbp;
  195.     DBT key, data;
  196.     register char *r_end, *rp, **db_p;
  197.     int myfd, eof, foundit, retval;
  198.     char *record;
  199.     int tc_not_resolved;
  200.     char pbuf[_POSIX_PATH_MAX];
  201.     
  202.     /*
  203.      * Return with ``loop detected'' error if we've recursed more than
  204.      * MAX_RECURSION times.
  205.      */
  206.     if (depth > MAX_RECURSION)
  207.         return (-3);
  208.  
  209.     /*
  210.      * Check if we have a top record from cgetset().
  211.          */
  212.     if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
  213.         if ((record = malloc (topreclen + BFRAG)) == NULL) {
  214.             errno = ENOMEM;
  215.             return (-2);
  216.         }
  217.         (void)strcpy(record, toprec);
  218.         myfd = 0;
  219.         db_p = db_array;
  220.         rp = record + topreclen + 1;
  221.         r_end = rp + BFRAG;
  222.         goto tc_exp;
  223.     }
  224.     /*
  225.      * Allocate first chunk of memory.
  226.      */
  227.     if ((record = malloc(BFRAG)) == NULL) {
  228.         errno = ENOMEM;
  229.         return (-2);
  230.     }
  231.     r_end = record + BFRAG;
  232.     foundit = 0;
  233.     /*
  234.      * Loop through database array until finding the record.
  235.      */
  236.  
  237.     for (db_p = db_array; *db_p != NULL; db_p++) {
  238.         eof = 0;
  239.  
  240.         /*
  241.          * Open database if not already open.
  242.          */
  243.  
  244.         if (fd >= 0) {
  245.             (void)lseek(fd, (off_t)0, L_SET);
  246.             myfd = 0;
  247.         } else {
  248.             (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
  249.             if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
  250.                  != NULL) {
  251.                 free(record);
  252.                 retval = cdbget(capdbp, &record, name);
  253.                 if (capdbp->close(capdbp) < 0)
  254.                     return (-2);
  255.                 *len = strlen(record);
  256.                 *cap = malloc(*len + 1);
  257.                 memmove(*cap, record, *len + 1);
  258.                 return (retval);
  259.             } else {
  260.                 fd = open(*db_p, O_RDONLY, 0);
  261.                 if (fd < 0) {
  262.                     /* No error on unfound file. */
  263.                     if (errno == ENOENT)
  264.                         continue;
  265.                     free(record);
  266.                     return (-2);
  267.                 }
  268.                 myfd = 1;
  269.             }
  270.         }
  271.         /*
  272.          * Find the requested capability record ...
  273.          */
  274.         {
  275.         char buf[BUFSIZ];
  276.         register char *b_end, *bp;
  277.         register int c;
  278.  
  279.         /*
  280.          * Loop invariants:
  281.          *    There is always room for one more character in record.
  282.          *    R_end always points just past end of record.
  283.          *    Rp always points just past last character in record.
  284.          *    B_end always points just past last character in buf.
  285.          *    Bp always points at next character in buf.
  286.          */
  287.         b_end = buf;
  288.         bp = buf;
  289.         for (;;) {
  290.  
  291.             /*
  292.              * Read in a line implementing (\, newline)
  293.              * line continuation.
  294.              */
  295.             rp = record;
  296.             for (;;) {
  297.                 if (bp >= b_end) {
  298.                     int n;
  299.         
  300.                     n = read(fd, buf, sizeof(buf));
  301.                     if (n <= 0) {
  302.                         if (myfd)
  303.                             (void)close(fd);
  304.                         if (n < 0) {
  305.                             free(record);
  306.                             return (-2);
  307.                         } else {
  308.                             fd = -1;
  309.                             eof = 1;
  310.                             break;
  311.                         }
  312.                     }
  313.                     b_end = buf+n;
  314.                     bp = buf;
  315.                 }
  316.     
  317.                 c = *bp++;
  318.                 if (c == '\n') {
  319.                     if (rp > record && *(rp-1) == '\\') {
  320.                         rp--;
  321.                         continue;
  322.                     } else
  323.                         break;
  324.                 }
  325.                 *rp++ = c;
  326.  
  327.                 /*
  328.                  * Enforce loop invariant: if no room 
  329.                  * left in record buffer, try to get
  330.                  * some more.
  331.                  */
  332.                 if (rp >= r_end) {
  333.                     u_int pos;
  334.                     size_t newsize;
  335.  
  336.                     pos = rp - record;
  337.                     newsize = r_end - record + BFRAG;
  338.                     record = realloc(record, newsize);
  339.                     if (record == NULL) {
  340.                         errno = ENOMEM;
  341.                         if (myfd)
  342.                             (void)close(fd);
  343.                         return (-2);
  344.                     }
  345.                     r_end = record + newsize;
  346.                     rp = record + pos;
  347.                 }
  348.             }
  349.                 /* loop invariant let's us do this */
  350.             *rp++ = '\0';
  351.  
  352.             /*
  353.              * If encountered eof check next file.
  354.              */
  355.             if (eof)
  356.                 break;
  357.                 
  358.             /*
  359.              * Toss blank lines and comments.
  360.              */
  361.             if (*record == '\0' || *record == '#')
  362.                 continue;
  363.     
  364.             /*
  365.              * See if this is the record we want ...
  366.              */
  367.             if (cgetmatch(record, name) == 0) {
  368.                 if (nfield == NULL || !nfcmp(nfield, record)) {
  369.                     foundit = 1;
  370.                     break;    /* found it! */
  371.                 }
  372.             }
  373.         }
  374.     }
  375.         if (foundit)
  376.             break;
  377.     }
  378.  
  379.     if (!foundit)
  380.         return (-1);
  381.  
  382.     /*
  383.      * Got the capability record, but now we have to expand all tc=name
  384.      * references in it ...
  385.      */
  386. tc_exp:    {
  387.         register char *newicap, *s;
  388.         register int newilen;
  389.         u_int ilen;
  390.         int diff, iret, tclen;
  391.         char *icap, *scan, *tc, *tcstart, *tcend;
  392.  
  393.         /*
  394.          * Loop invariants:
  395.          *    There is room for one more character in record.
  396.          *    R_end points just past end of record.
  397.          *    Rp points just past last character in record.
  398.          *    Scan points at remainder of record that needs to be
  399.          *    scanned for tc=name constructs.
  400.          */
  401.         scan = record;
  402.         tc_not_resolved = 0;
  403.         for (;;) {
  404.             if ((tc = cgetcap(scan, "tc", '=')) == NULL)
  405.                 break;
  406.  
  407.             /*
  408.              * Find end of tc=name and stomp on the trailing `:'
  409.              * (if present) so we can use it to call ourselves.
  410.              */
  411.             s = tc;
  412.             for (;;)
  413.                 if (*s == '\0')
  414.                     break;
  415.                 else
  416.                     if (*s++ == ':') {
  417.                         *(s - 1) = '\0';
  418.                         break;
  419.                     }
  420.             tcstart = tc - 3;
  421.             tclen = s - tcstart;
  422.             tcend = s;
  423.  
  424.             iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 
  425.                       NULL);
  426.             newicap = icap;        /* Put into a register. */
  427.             newilen = ilen;
  428.             if (iret != 0) {
  429.                 /* an error */
  430.                 if (iret < -1) {
  431.                     if (myfd)
  432.                         (void)close(fd);
  433.                     free(record);
  434.                     return (iret);
  435.                 }
  436.                 if (iret == 1)
  437.                     tc_not_resolved = 1;
  438.                 /* couldn't resolve tc */
  439.                 if (iret == -1) {
  440.                     *(s - 1) = ':';            
  441.                     scan = s - 1;
  442.                     tc_not_resolved = 1;
  443.                     continue;
  444.                     
  445.                 }
  446.             }
  447.             /* not interested in name field of tc'ed record */
  448.             s = newicap;
  449.             for (;;)
  450.                 if (*s == '\0')
  451.                     break;
  452.                 else
  453.                     if (*s++ == ':')
  454.                         break;
  455.             newilen -= s - newicap;
  456.             newicap = s;
  457.  
  458.             /* make sure interpolated record is `:'-terminated */
  459.             s += newilen;
  460.             if (*(s-1) != ':') {
  461.                 *s = ':';    /* overwrite NUL with : */
  462.                 newilen++;
  463.             }
  464.  
  465.             /*
  466.              * Make sure there's enough room to insert the
  467.              * new record.
  468.              */
  469.             diff = newilen - tclen;
  470.             if (diff >= r_end - rp) {
  471.                 u_int pos, tcpos, tcposend;
  472.                 size_t newsize;
  473.  
  474.                 pos = rp - record;
  475.                 newsize = r_end - record + diff + BFRAG;
  476.                 tcpos = tcstart - record;
  477.                 tcposend = tcend - record;
  478.                 record = realloc(record, newsize);
  479.                 if (record == NULL) {
  480.                     errno = ENOMEM;
  481.                     if (myfd)
  482.                         (void)close(fd);
  483.                     free(icap);
  484.                     return (-2);
  485.                 }
  486.                 r_end = record + newsize;
  487.                 rp = record + pos;
  488.                 tcstart = record + tcpos;
  489.                 tcend = record + tcposend;
  490.             }
  491.  
  492.             /*
  493.              * Insert tc'ed record into our record.
  494.              */
  495.             s = tcstart + newilen;
  496.             bcopy(tcend, s, rp - tcend);
  497.             bcopy(newicap, tcstart, newilen);
  498.             rp += diff;
  499.             free(icap);
  500.  
  501.             /*
  502.              * Start scan on `:' so next cgetcap works properly
  503.              * (cgetcap always skips first field).
  504.              */
  505.             scan = s-1;
  506.         }
  507.     
  508.     }
  509.     /*
  510.      * Close file (if we opened it), give back any extra memory, and
  511.      * return capability, length and success.
  512.      */
  513.     if (myfd)
  514.         (void)close(fd);
  515.     *len = rp - record - 1;    /* don't count NUL */
  516.     if (r_end > rp)
  517.         if ((record = 
  518.              realloc(record, (size_t)(rp - record))) == NULL) {
  519.             errno = ENOMEM;
  520.             return (-2);
  521.         }
  522.         
  523.     *cap = record;
  524.     if (tc_not_resolved)
  525.         return (1);
  526.     return (0);
  527. }    
  528.  
  529. static int
  530. cdbget(capdbp, bp, name)
  531.     DB *capdbp;
  532.     char **bp, *name;
  533. {
  534.     DBT key, data;
  535.     char *buf;
  536.     int st;
  537.  
  538.     key.data = name;
  539.     key.size = strlen(name);
  540.  
  541.     for (;;) {
  542.         /* Get the reference. */
  543.         switch(capdbp->get(capdbp, &key, &data, 0)) {
  544.         case -1:
  545.             return (-2);
  546.         case 1:
  547.             return (-1);
  548.         }
  549.  
  550.         /* If not an index to another record, leave. */
  551.         if (((char *)data.data)[0] != SHADOW)
  552.             break;
  553.  
  554.         key.data = (char *)data.data + 1;
  555.         key.size = data.size - 1;
  556.     }
  557.     
  558.     *bp = (char *)data.data + 1;
  559.     return (((char *)(data.data))[0] == TCERR ? 1 : 0);
  560. }
  561.  
  562. /*
  563.  * Cgetmatch will return 0 if name is one of the names of the capability
  564.  * record buf, -1 if not.
  565.  */
  566. int
  567. cgetmatch(buf, name)
  568.     char *buf, *name;
  569. {
  570.     register char *np, *bp;
  571.  
  572.     /*
  573.      * Start search at beginning of record.
  574.      */
  575.     bp = buf;
  576.     for (;;) {
  577.         /*
  578.          * Try to match a record name.
  579.          */
  580.         np = name;
  581.         for (;;)
  582.             if (*np == '\0')
  583.                 if (*bp == '|' || *bp == ':' || *bp == '\0')
  584.                     return (0);
  585.                 else
  586.                     break;
  587.             else
  588.                 if (*bp++ != *np++)
  589.                     break;
  590.  
  591.         /*
  592.          * Match failed, skip to next name in record.
  593.          */
  594.         bp--;    /* a '|' or ':' may have stopped the match */
  595.         for (;;)
  596.             if (*bp == '\0' || *bp == ':')
  597.                 return (-1);    /* match failed totally */
  598.             else
  599.                 if (*bp++ == '|')
  600.                     break;    /* found next name */
  601.     }
  602. }
  603.  
  604.  
  605.  
  606.  
  607.  
  608. int
  609. cgetfirst(buf, db_array)
  610.     char **buf, **db_array;
  611. {
  612.     (void)cgetclose();
  613.     return (cgetnext(buf, db_array));
  614. }
  615.  
  616. static FILE *pfp;
  617. static int slash;
  618. static char **dbp;
  619.  
  620. int
  621. cgetclose()
  622. {
  623.     if (pfp != NULL) {
  624.         (void)fclose(pfp);
  625.         pfp = NULL;
  626.     }
  627.     dbp = NULL;
  628.     gottoprec = 0;
  629.     slash = 0;
  630.     return(0);
  631. }
  632.  
  633. /*
  634.  * Cgetnext() gets either the first or next entry in the logical database 
  635.  * specified by db_array.  It returns 0 upon completion of the database, 1
  636.  * upon returning an entry with more remaining, and -1 if an error occurs.
  637.  */
  638. int
  639. cgetnext(bp, db_array)
  640.         register char **bp;
  641.     char **db_array;
  642. {
  643.     size_t len;
  644.     int status, i, done;
  645.     char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
  646.     u_int dummy;
  647.  
  648.     if (dbp == NULL)
  649.         dbp = db_array;
  650.  
  651.     if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
  652.         (void)cgetclose();
  653.         return (-1);
  654.     }
  655.     for(;;) {
  656.         if (toprec && !gottoprec) {
  657.             gottoprec = 1;
  658.             line = toprec;
  659.         } else {
  660.             line = fgetline(pfp, &len);
  661.             if (line == NULL && pfp) {
  662.                 (void)fclose(pfp);
  663.                 if (ferror(pfp)) {
  664.                     (void)cgetclose();
  665.                     return (-1);
  666.                 } else {
  667.                     if (*++dbp == NULL) {
  668.                         (void)cgetclose();
  669.                         return (0);
  670.                     } else if ((pfp =
  671.                         fopen(*dbp, "r")) == NULL) {
  672.                         (void)cgetclose();
  673.                         return (-1);
  674.                     } else
  675.                         continue;
  676.                 }
  677.             } else
  678.                 line[len - 1] = '\0';
  679.             if (len == 1) {
  680.                 slash = 0;
  681.                 continue;
  682.             }
  683.             if (isspace(*line) ||
  684.                 *line == ':' || *line == '#' || slash) {
  685.                 if (line[len - 2] == '\\')
  686.                     slash = 1;
  687.                 else
  688.                     slash = 0;
  689.                 continue;
  690.             }
  691.             if (line[len - 2] == '\\')
  692.                 slash = 1;
  693.             else
  694.                 slash = 0;
  695.         }            
  696.  
  697.  
  698.         /* 
  699.          * Line points to a name line.
  700.          */
  701.         i = 0;
  702.         done = 0;
  703.         np = nbuf;
  704.         for (;;) {
  705.             for (cp = line; *cp != '\0'; cp++) {
  706.                 if (*cp == ':') {
  707.                     *np++ = ':';
  708.                     done = 1;
  709.                     break;
  710.                 }
  711.                 if (*cp == '\\')
  712.                     break;
  713.                 *np++ = *cp;
  714.             }
  715.             if (done) {
  716.                 *np = '\0';
  717.                 break;
  718.             } else { /* name field extends beyond the line */
  719.                 line = fgetline(pfp, &len);
  720.                 if (line == NULL && pfp) {
  721.                     (void)fclose(pfp);
  722.                     if (ferror(pfp)) {
  723.                         (void)cgetclose();
  724.                         return (-1);
  725.                     }
  726.                 } else
  727.                     line[len - 1] = '\0';
  728.             }
  729.         }
  730.         rp = buf;
  731.         for(cp = nbuf; *cp != NULL; cp++)
  732.             if (*cp == '|' || *cp == ':')
  733.                 break;
  734.             else
  735.                 *rp++ = *cp;
  736.  
  737.         *rp = '\0';
  738.         /* 
  739.          * XXX 
  740.          * Last argument of getent here should be nbuf if we want true
  741.          * sequential access in the case of duplicates.  
  742.          * With NULL, getent will return the first entry found
  743.          * rather than the duplicate entry record.  This is a 
  744.          * matter of semantics that should be resolved.
  745.          */
  746.         status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
  747.         if (status == -2 || status == -3)
  748.             (void)cgetclose();
  749.  
  750.         return (status + 1);
  751.     }
  752.     /* NOTREACHED */
  753. }
  754.  
  755. /*
  756.  * Cgetstr retrieves the value of the string capability cap from the
  757.  * capability record pointed to by buf.  A pointer to a decoded, NUL
  758.  * terminated, malloc'd copy of the string is returned in the char *
  759.  * pointed to by str.  The length of the string not including the trailing
  760.  * NUL is returned on success, -1 if the requested string capability
  761.  * couldn't be found, -2 if a system error was encountered (storage
  762.  * allocation failure).
  763.  */
  764. int
  765. cgetstr(buf, cap, str)
  766.     char *buf, *cap;
  767.     char **str;
  768. {
  769.     register u_int m_room;
  770.     register char *bp, *mp;
  771.     int len;
  772.     char *mem;
  773.  
  774.     /*
  775.      * Find string capability cap
  776.      */
  777.     bp = cgetcap(buf, cap, '=');
  778.     if (bp == NULL)
  779.         return (-1);
  780.  
  781.     /*
  782.      * Conversion / storage allocation loop ...  Allocate memory in
  783.      * chunks SFRAG in size.
  784.      */
  785.     if ((mem = malloc(SFRAG)) == NULL) {
  786.         errno = ENOMEM;
  787.         return (-2);    /* couldn't even allocate the first fragment */
  788.     }
  789.     m_room = SFRAG;
  790.     mp = mem;
  791.  
  792.     while (*bp != ':' && *bp != '\0') {
  793.         /*
  794.          * Loop invariants:
  795.          *    There is always room for one more character in mem.
  796.          *    Mp always points just past last character in mem.
  797.          *    Bp always points at next character in buf.
  798.          */
  799.         if (*bp == '^') {
  800.             bp++;
  801.             if (*bp == ':' || *bp == '\0')
  802.                 break;    /* drop unfinished escape */
  803.             *mp++ = *bp++ & 037;
  804.         } else if (*bp == '\\') {
  805.             bp++;
  806.             if (*bp == ':' || *bp == '\0')
  807.                 break;    /* drop unfinished escape */
  808.             if ('0' <= *bp && *bp <= '7') {
  809.                 register int n, i;
  810.  
  811.                 n = 0;
  812.                 i = 3;    /* maximum of three octal digits */
  813.                 do {
  814.                     n = n * 8 + (*bp++ - '0');
  815.                 } while (--i && '0' <= *bp && *bp <= '7');
  816.                 *mp++ = n;
  817.             }
  818.             else switch (*bp++) {
  819.                 case 'b': case 'B':
  820.                     *mp++ = '\b';
  821.                     break;
  822.                 case 't': case 'T':
  823.                     *mp++ = '\t';
  824.                     break;
  825.                 case 'n': case 'N':
  826.                     *mp++ = '\n';
  827.                     break;
  828.                 case 'f': case 'F':
  829.                     *mp++ = '\f';
  830.                     break;
  831.                 case 'r': case 'R':
  832.                     *mp++ = '\r';
  833.                     break;
  834.                 case 'e': case 'E':
  835.                     *mp++ = ESC;
  836.                     break;
  837.                 case 'c': case 'C':
  838.                     *mp++ = ':';
  839.                     break;
  840.                 default:
  841.                     /*
  842.                      * Catches '\', '^', and
  843.                      *  everything else.
  844.                      */
  845.                     *mp++ = *(bp-1);
  846.                     break;
  847.             }
  848.         } else
  849.             *mp++ = *bp++;
  850.         m_room--;
  851.  
  852.         /*
  853.          * Enforce loop invariant: if no room left in current
  854.          * buffer, try to get some more.
  855.          */
  856.         if (m_room == 0) {
  857.             size_t size = mp - mem;
  858.  
  859.             if ((mem = realloc(mem, size + SFRAG)) == NULL)
  860.                 return (-2);
  861.             m_room = SFRAG;
  862.             mp = mem + size;
  863.         }
  864.     }
  865.     *mp++ = '\0';    /* loop invariant let's us do this */
  866.     m_room--;
  867.     len = mp - mem - 1;
  868.  
  869.     /*
  870.      * Give back any extra memory and return value and success.
  871.      */
  872.     if (m_room != 0)
  873.         if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
  874.             return (-2);
  875.     *str = mem;
  876.     return (len);
  877. }
  878.  
  879. /*
  880.  * Cgetustr retrieves the value of the string capability cap from the
  881.  * capability record pointed to by buf.  The difference between cgetustr()
  882.  * and cgetstr() is that cgetustr does not decode escapes but rather treats
  883.  * all characters literally.  A pointer to a  NUL terminated malloc'd 
  884.  * copy of the string is returned in the char pointed to by str.  The 
  885.  * length of the string not including the trailing NUL is returned on success,
  886.  * -1 if the requested string capability couldn't be found, -2 if a system 
  887.  * error was encountered (storage allocation failure).
  888.  */
  889. int
  890. cgetustr(buf, cap, str)
  891.     char *buf, *cap, **str;
  892. {
  893.     register u_int m_room;
  894.     register char *bp, *mp;
  895.     int len;
  896.     char *mem;
  897.  
  898.     /*
  899.      * Find string capability cap
  900.      */
  901.     if ((bp = cgetcap(buf, cap, '=')) == NULL)
  902.         return (-1);
  903.  
  904.     /*
  905.      * Conversion / storage allocation loop ...  Allocate memory in
  906.      * chunks SFRAG in size.
  907.      */
  908.     if ((mem = malloc(SFRAG)) == NULL) {
  909.         errno = ENOMEM;
  910.         return (-2);    /* couldn't even allocate the first fragment */
  911.     }
  912.     m_room = SFRAG;
  913.     mp = mem;
  914.  
  915.     while (*bp != ':' && *bp != '\0') {
  916.         /*
  917.          * Loop invariants:
  918.          *    There is always room for one more character in mem.
  919.          *    Mp always points just past last character in mem.
  920.          *    Bp always points at next character in buf.
  921.          */
  922.         *mp++ = *bp++;
  923.         m_room--;
  924.  
  925.         /*
  926.          * Enforce loop invariant: if no room left in current
  927.          * buffer, try to get some more.
  928.          */
  929.         if (m_room == 0) {
  930.             size_t size = mp - mem;
  931.  
  932.             if ((mem = realloc(mem, size + SFRAG)) == NULL)
  933.                 return (-2);
  934.             m_room = SFRAG;
  935.             mp = mem + size;
  936.         }
  937.     }
  938.     *mp++ = '\0';    /* loop invariant let's us do this */
  939.     m_room--;
  940.     len = mp - mem - 1;
  941.  
  942.     /*
  943.      * Give back any extra memory and return value and success.
  944.      */
  945.     if (m_room != 0)
  946.         if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
  947.             return (-2);
  948.     *str = mem;
  949.     return (len);
  950. }
  951.  
  952. /*
  953.  * Cgetnum retrieves the value of the numeric capability cap from the
  954.  * capability record pointed to by buf.  The numeric value is returned in
  955.  * the long pointed to by num.  0 is returned on success, -1 if the requested
  956.  * numeric capability couldn't be found.
  957.  */
  958. int
  959. cgetnum(buf, cap, num)
  960.     char *buf, *cap;
  961.     long *num;
  962. {
  963.     register long n;
  964.     register int base, digit;
  965.     register char *bp;
  966.  
  967.     /*
  968.      * Find numeric capability cap
  969.      */
  970.     bp = cgetcap(buf, cap, '#');
  971.     if (bp == NULL)
  972.         return (-1);
  973.  
  974.     /*
  975.      * Look at value and determine numeric base:
  976.      *    0x... or 0X...    hexadecimal,
  977.      * else    0...        octal,
  978.      * else            decimal.
  979.      */
  980.     if (*bp == '0') {
  981.         bp++;
  982.         if (*bp == 'x' || *bp == 'X') {
  983.             bp++;
  984.             base = 16;
  985.         } else
  986.             base = 8;
  987.     } else
  988.         base = 10;
  989.  
  990.     /*
  991.      * Conversion loop ...
  992.      */
  993.     n = 0;
  994.     for (;;) {
  995.         if ('0' <= *bp && *bp <= '9')
  996.             digit = *bp - '0';
  997.         else if ('a' <= *bp && *bp <= 'f')
  998.             digit = 10 + *bp - 'a';
  999.         else if ('A' <= *bp && *bp <= 'F')
  1000.             digit = 10 + *bp - 'A';
  1001.         else
  1002.             break;
  1003.  
  1004.         if (digit >= base)
  1005.             break;
  1006.  
  1007.         n = n * base + digit;
  1008.         bp++;
  1009.     }
  1010.  
  1011.     /*
  1012.      * Return value and success.
  1013.      */
  1014.     *num = n;
  1015.     return (0);
  1016. }
  1017.  
  1018.  
  1019. /*
  1020.  * Compare name field of record.
  1021.  */
  1022. static int
  1023. nfcmp(nf, rec)
  1024.     char *nf, *rec;
  1025. {
  1026.     char *cp, tmp;
  1027.     int ret;
  1028.     
  1029.     for (cp = rec; *cp != ':'; cp++)
  1030.         ;
  1031.     
  1032.     tmp = *(cp + 1);
  1033.     *(cp + 1) = '\0';
  1034.     ret = strcmp(nf, rec);
  1035.     *(cp + 1) = tmp;
  1036.  
  1037.     return (ret);
  1038. }
  1039.