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