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