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