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