cf3081afa29fb37299af79fac8ff097c31eb7e05
[games.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.4 2004/06/06 15:05:55 hmp Exp $
38  *
39  * @(#)getcap.c 8.3 (Berkeley) 3/25/94
40  */
41
42 #include <sys/types.h>
43
44 #include <ctype.h>
45 #include <db.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
54 #define BFRAG           1024
55 #define BSIZE           1024
56 #define ESC             ('[' & 037)     /* ASCII ESC */
57 #define MAX_RECURSION   32              /* maximum getent recursion */
58 #define SFRAG           100             /* cgetstr mallocs in SFRAG chunks */
59
60 #define RECOK   (char)0
61 #define TCERR   (char)1
62 #define SHADOW  (char)2
63
64 static size_t    topreclen;     /* toprec length */
65 static char     *toprec;        /* Additional record specified by cgetset() */
66 static int       gottoprec;     /* Flag indicating retrieval of toprecord */
67
68 static int      cdbget (DB *, char **, char *);
69 static int      getent (char **, u_int *, char **, int, char *, int, char *);
70 static int      nfcmp (char *, char *);
71
72 /*
73  * Cgetset() allows the addition of a user specified buffer to be added
74  * to the database array, in effect "pushing" the buffer on top of the
75  * virtual database. 0 is returned on success, -1 on failure.
76  */
77 int
78 cgetset(ent)
79         char *ent;
80 {
81         if (ent == NULL) {
82                 if (toprec)
83                         free(toprec);
84                 toprec = NULL;
85                 topreclen = 0;
86                 return (0);
87         }
88         topreclen = strlen(ent);
89         if ((toprec = malloc (topreclen + 1)) == NULL) {
90                 errno = ENOMEM;
91                 return (-1);
92         }
93         gottoprec = 0;
94         (void)strcpy(toprec, ent);
95         return (0);
96 }
97
98 /*
99  * Cgetcap searches the capability record buf for the capability cap with
100  * type `type'.  A pointer to the value of cap is returned on success, NULL
101  * if the requested capability couldn't be found.
102  *
103  * Specifying a type of ':' means that nothing should follow cap (:cap:).
104  * In this case a pointer to the terminating ':' or NUL will be returned if
105  * cap is found.
106  *
107  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
108  * return NULL.
109  */
110 char *
111 cgetcap(buf, cap, type)
112         char *buf, *cap;
113         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(buf, db_array, name)
164         char **buf, **db_array, *name;
165 {
166         u_int dummy;
167
168         return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
169 }
170
171 /*
172  * Getent implements the functions of cgetent.  If fd is non-negative,
173  * *db_array has already been opened and fd is the open file descriptor.  We
174  * do this to save time and avoid using up file descriptors for tc=
175  * recursions.
176  *
177  * Getent returns the same success/failure codes as cgetent.  On success, a
178  * pointer to a malloc'ed capability record with all tc= capabilities fully
179  * expanded and its length (not including trailing ASCII NUL) are left in
180  * *cap and *len.
181  *
182  * Basic algorithm:
183  *      + Allocate memory incrementally as needed in chunks of size BFRAG
184  *        for capability buffer.
185  *      + Recurse for each tc=name and interpolate result.  Stop when all
186  *        names interpolated, a name can't be found, or depth exceeds
187  *        MAX_RECURSION.
188  */
189 static int
190 getent(cap, len, db_array, fd, name, depth, nfield)
191         char **cap, **db_array, *name, *nfield;
192         u_int *len;
193         int fd, depth;
194 {
195         DB *capdbp;
196         char *r_end, *rp, **db_p;
197         int myfd, eof, foundit, retval, clen;
198         char *record, *cbuf;
199         int tc_not_resolved;
200         char pbuf[_POSIX_PATH_MAX];
201
202         /*
203          * Return with ``loop detected'' error if we've recursed more than
204          * MAX_RECURSION times.
205          */
206         if (depth > MAX_RECURSION)
207                 return (-3);
208
209         /*
210          * Check if we have a top record from cgetset().
211          */
212         if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
213                 if ((record = malloc (topreclen + BFRAG)) == NULL) {
214                         errno = ENOMEM;
215                         return (-2);
216                 }
217                 (void)strcpy(record, toprec);
218                 myfd = 0;
219                 db_p = db_array;
220                 rp = record + topreclen + 1;
221                 r_end = rp + BFRAG;
222                 goto tc_exp;
223         }
224         /*
225          * Allocate first chunk of memory.
226          */
227         if ((record = malloc(BFRAG)) == NULL) {
228                 errno = ENOMEM;
229                 return (-2);
230         }
231         r_end = record + BFRAG;
232         foundit = 0;
233         /*
234          * Loop through database array until finding the record.
235          */
236
237         for (db_p = db_array; *db_p != NULL; db_p++) {
238                 eof = 0;
239
240                 /*
241                  * Open database if not already open.
242                  */
243
244                 if (fd >= 0) {
245                         (void)lseek(fd, (off_t)0, SEEK_SET);
246                         myfd = 0;
247                 } else {
248                         (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
249                         if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
250                              != NULL) {
251                                 free(record);
252                                 retval = cdbget(capdbp, &record, name);
253                                 if (retval < 0) {
254                                         /* no record available */
255                                         (void)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                                                         (void)_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                                                         (void)_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                                                 (void)_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                                                 (void)_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                 (void)_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(capdbp, bp, name)
538         DB *capdbp;
539         char **bp, *name;
540 {
541         DBT key, data;
542
543         key.data = name;
544         key.size = strlen(name);
545
546         for (;;) {
547                 /* Get the reference. */
548                 switch(capdbp->get(capdbp, &key, &data, 0)) {
549                 case -1:
550                         return (-2);
551                 case 1:
552                         return (-1);
553                 }
554
555                 /* If not an index to another record, leave. */
556                 if (((char *)data.data)[0] != SHADOW)
557                         break;
558
559                 key.data = (char *)data.data + 1;
560                 key.size = data.size - 1;
561         }
562
563         *bp = (char *)data.data + 1;
564         return (((char *)(data.data))[0] == TCERR ? 1 : 0);
565 }
566
567 /*
568  * Cgetmatch will return 0 if name is one of the names of the capability
569  * record buf, -1 if not.
570  */
571 int
572 cgetmatch(buf, name)
573         char *buf, *name;
574 {
575         char *np, *bp;
576
577         /*
578          * Start search at beginning of record.
579          */
580         bp = buf;
581         for (;;) {
582                 /*
583                  * Try to match a record name.
584                  */
585                 np = name;
586                 for (;;)
587                         if (*np == '\0')
588                                 if (*bp == '|' || *bp == ':' || *bp == '\0')
589                                         return (0);
590                                 else
591                                         break;
592                         else
593                                 if (*bp++ != *np++)
594                                         break;
595
596                 /*
597                  * Match failed, skip to next name in record.
598                  */
599                 bp--;   /* a '|' or ':' may have stopped the match */
600                 for (;;)
601                         if (*bp == '\0' || *bp == ':')
602                                 return (-1);    /* match failed totally */
603                         else
604                                 if (*bp++ == '|')
605                                         break;  /* found next name */
606         }
607 }
608
609
610
611
612
613 int
614 cgetfirst(buf, db_array)
615         char **buf, **db_array;
616 {
617         (void)cgetclose();
618         return (cgetnext(buf, db_array));
619 }
620
621 static FILE *pfp;
622 static int slash;
623 static char **dbp;
624
625 int
626 cgetclose()
627 {
628         if (pfp != NULL) {
629                 (void)fclose(pfp);
630                 pfp = NULL;
631         }
632         dbp = NULL;
633         gottoprec = 0;
634         slash = 0;
635         return(0);
636 }
637
638 /*
639  * Cgetnext() gets either the first or next entry in the logical database
640  * specified by db_array.  It returns 0 upon completion of the database, 1
641  * upon returning an entry with more remaining, and -1 if an error occurs.
642  */
643 int
644 cgetnext(bp, db_array)
645         char **bp;
646         char **db_array;
647 {
648         size_t len;
649         int done, hadreaderr, i, savederrno, status;
650         char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
651         u_int dummy;
652
653         if (dbp == NULL)
654                 dbp = db_array;
655
656         if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
657                 (void)cgetclose();
658                 return (-1);
659         }
660         for(;;) {
661                 if (toprec && !gottoprec) {
662                         gottoprec = 1;
663                         line = toprec;
664                 } else {
665                         line = fgetln(pfp, &len);
666                         if (line == NULL && pfp) {
667                                 hadreaderr = ferror(pfp);
668                                 if (hadreaderr)
669                                         savederrno = errno;
670                                 fclose(pfp);
671                                 pfp = NULL;
672                                 if (hadreaderr) {
673                                         cgetclose();
674                                         errno = savederrno;
675                                         return (-1);
676                                 } else {
677                                         if (*++dbp == NULL) {
678                                                 (void)cgetclose();
679                                                 return (0);
680                                         } else if ((pfp =
681                                             fopen(*dbp, "r")) == NULL) {
682                                                 (void)cgetclose();
683                                                 return (-1);
684                                         } else
685                                                 continue;
686                                 }
687                         } else
688                                 line[len - 1] = '\0';
689                         if (len == 1) {
690                                 slash = 0;
691                                 continue;
692                         }
693                         if (isspace((unsigned char)*line) ||
694                             *line == ':' || *line == '#' || slash) {
695                                 if (line[len - 2] == '\\')
696                                         slash = 1;
697                                 else
698                                         slash = 0;
699                                 continue;
700                         }
701                         if (line[len - 2] == '\\')
702                                 slash = 1;
703                         else
704                                 slash = 0;
705                 }
706
707
708                 /*
709                  * Line points to a name line.
710                  */
711                 i = 0;
712                 done = 0;
713                 np = nbuf;
714                 for (;;) {
715                         for (cp = line; *cp != '\0'; cp++) {
716                                 if (*cp == ':') {
717                                         *np++ = ':';
718                                         done = 1;
719                                         break;
720                                 }
721                                 if (*cp == '\\')
722                                         break;
723                                 *np++ = *cp;
724                         }
725                         if (done) {
726                                 *np = '\0';
727                                 break;
728                         } else { /* name field extends beyond the line */
729                                 line = fgetln(pfp, &len);
730                                 if (line == NULL && pfp) {
731                                         /* Name extends beyond the EOF! */
732                                         hadreaderr = ferror(pfp);
733                                         if (hadreaderr)
734                                                 savederrno = errno;
735                                         fclose(pfp);
736                                         pfp = NULL;
737                                         if (hadreaderr) {
738                                                 cgetclose();
739                                                 errno = savederrno;
740                                                 return (-1);
741                                         } else {
742                                                 cgetclose();
743                                                 return (-1);
744                                         }
745                                 } else
746                                         line[len - 1] = '\0';
747                         }
748                 }
749                 rp = buf;
750                 for(cp = nbuf; *cp != '\0'; cp++)
751                         if (*cp == '|' || *cp == ':')
752                                 break;
753                         else
754                                 *rp++ = *cp;
755
756                 *rp = '\0';
757                 /*
758                  * XXX
759                  * Last argument of getent here should be nbuf if we want true
760                  * sequential access in the case of duplicates.
761                  * With NULL, getent will return the first entry found
762                  * rather than the duplicate entry record.  This is a
763                  * matter of semantics that should be resolved.
764                  */
765                 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
766                 if (status == -2 || status == -3)
767                         (void)cgetclose();
768
769                 return (status + 1);
770         }
771         /* NOTREACHED */
772 }
773
774 /*
775  * Cgetstr retrieves the value of the string capability cap from the
776  * capability record pointed to by buf.  A pointer to a decoded, NUL
777  * terminated, malloc'd copy of the string is returned in the char *
778  * pointed to by str.  The length of the string not including the trailing
779  * NUL is returned on success, -1 if the requested string capability
780  * couldn't be found, -2 if a system error was encountered (storage
781  * allocation failure).
782  */
783 int
784 cgetstr(buf, cap, str)
785         char *buf, *cap;
786         char **str;
787 {
788         u_int m_room;
789         char *bp, *mp;
790         int len;
791         char *mem;
792
793         /*
794          * Find string capability cap
795          */
796         bp = cgetcap(buf, cap, '=');
797         if (bp == NULL)
798                 return (-1);
799
800         /*
801          * Conversion / storage allocation loop ...  Allocate memory in
802          * chunks SFRAG in size.
803          */
804         if ((mem = malloc(SFRAG)) == NULL) {
805                 errno = ENOMEM;
806                 return (-2);    /* couldn't even allocate the first fragment */
807         }
808         m_room = SFRAG;
809         mp = mem;
810
811         while (*bp != ':' && *bp != '\0') {
812                 /*
813                  * Loop invariants:
814                  *      There is always room for one more character in mem.
815                  *      Mp always points just past last character in mem.
816                  *      Bp always points at next character in buf.
817                  */
818                 if (*bp == '^') {
819                         bp++;
820                         if (*bp == ':' || *bp == '\0')
821                                 break;  /* drop unfinished escape */
822                         if (*bp == '?') {
823                                 *mp++ = '\177';
824                                 bp++;
825                         } else
826                                 *mp++ = *bp++ & 037;
827                 } else if (*bp == '\\') {
828                         bp++;
829                         if (*bp == ':' || *bp == '\0')
830                                 break;  /* drop unfinished escape */
831                         if ('0' <= *bp && *bp <= '7') {
832                                 int n, i;
833
834                                 n = 0;
835                                 i = 3;  /* maximum of three octal digits */
836                                 do {
837                                         n = n * 8 + (*bp++ - '0');
838                                 } while (--i && '0' <= *bp && *bp <= '7');
839                                 *mp++ = n;
840                         }
841                         else switch (*bp++) {
842                                 case 'b': case 'B':
843                                         *mp++ = '\b';
844                                         break;
845                                 case 't': case 'T':
846                                         *mp++ = '\t';
847                                         break;
848                                 case 'n': case 'N':
849                                         *mp++ = '\n';
850                                         break;
851                                 case 'f': case 'F':
852                                         *mp++ = '\f';
853                                         break;
854                                 case 'r': case 'R':
855                                         *mp++ = '\r';
856                                         break;
857                                 case 'e': case 'E':
858                                         *mp++ = ESC;
859                                         break;
860                                 case 'c': case 'C':
861                                         *mp++ = ':';
862                                         break;
863                                 default:
864                                         /*
865                                          * Catches '\', '^', and
866                                          *  everything else.
867                                          */
868                                         *mp++ = *(bp-1);
869                                         break;
870                         }
871                 } else
872                         *mp++ = *bp++;
873                 m_room--;
874
875                 /*
876                  * Enforce loop invariant: if no room left in current
877                  * buffer, try to get some more.
878                  */
879                 if (m_room == 0) {
880                         size_t size = mp - mem;
881
882                         if ((mem = reallocf(mem, size + SFRAG)) == NULL)
883                                 return (-2);
884                         m_room = SFRAG;
885                         mp = mem + size;
886                 }
887         }
888         *mp++ = '\0';   /* loop invariant let's us do this */
889         m_room--;
890         len = mp - mem - 1;
891
892         /*
893          * Give back any extra memory and return value and success.
894          */
895         if (m_room != 0)
896                 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
897                         return (-2);
898         *str = mem;
899         return (len);
900 }
901
902 /*
903  * Cgetustr retrieves the value of the string capability cap from the
904  * capability record pointed to by buf.  The difference between cgetustr()
905  * and cgetstr() is that cgetustr does not decode escapes but rather treats
906  * all characters literally.  A pointer to a  NUL terminated malloc'd
907  * copy of the string is returned in the char pointed to by str.  The
908  * length of the string not including the trailing NUL is returned on success,
909  * -1 if the requested string capability couldn't be found, -2 if a system
910  * error was encountered (storage allocation failure).
911  */
912 int
913 cgetustr(buf, cap, str)
914         char *buf, *cap, **str;
915 {
916         u_int m_room;
917         char *bp, *mp;
918         int len;
919         char *mem;
920
921         /*
922          * Find string capability cap
923          */
924         if ((bp = cgetcap(buf, cap, '=')) == NULL)
925                 return (-1);
926
927         /*
928          * Conversion / storage allocation loop ...  Allocate memory in
929          * chunks SFRAG in size.
930          */
931         if ((mem = malloc(SFRAG)) == NULL) {
932                 errno = ENOMEM;
933                 return (-2);    /* couldn't even allocate the first fragment */
934         }
935         m_room = SFRAG;
936         mp = mem;
937
938         while (*bp != ':' && *bp != '\0') {
939                 /*
940                  * Loop invariants:
941                  *      There is always room for one more character in mem.
942                  *      Mp always points just past last character in mem.
943                  *      Bp always points at next character in buf.
944                  */
945                 *mp++ = *bp++;
946                 m_room--;
947
948                 /*
949                  * Enforce loop invariant: if no room left in current
950                  * buffer, try to get some more.
951                  */
952                 if (m_room == 0) {
953                         size_t size = mp - mem;
954
955                         if ((mem = reallocf(mem, size + SFRAG)) == NULL)
956                                 return (-2);
957                         m_room = SFRAG;
958                         mp = mem + size;
959                 }
960         }
961         *mp++ = '\0';   /* loop invariant let's us do this */
962         m_room--;
963         len = mp - mem - 1;
964
965         /*
966          * Give back any extra memory and return value and success.
967          */
968         if (m_room != 0)
969                 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
970                         return (-2);
971         *str = mem;
972         return (len);
973 }
974
975 /*
976  * Cgetnum retrieves the value of the numeric capability cap from the
977  * capability record pointed to by buf.  The numeric value is returned in
978  * the long pointed to by num.  0 is returned on success, -1 if the requested
979  * numeric capability couldn't be found.
980  */
981 int
982 cgetnum(buf, cap, num)
983         char *buf, *cap;
984         long *num;
985 {
986         long n;
987         int base, digit;
988         char *bp;
989
990         /*
991          * Find numeric capability cap
992          */
993         bp = cgetcap(buf, cap, '#');
994         if (bp == NULL)
995                 return (-1);
996
997         /*
998          * Look at value and determine numeric base:
999          *      0x... or 0X...  hexadecimal,
1000          * else 0...            octal,
1001          * else                 decimal.
1002          */
1003         if (*bp == '0') {
1004                 bp++;
1005                 if (*bp == 'x' || *bp == 'X') {
1006                         bp++;
1007                         base = 16;
1008                 } else
1009                         base = 8;
1010         } else
1011                 base = 10;
1012
1013         /*
1014          * Conversion loop ...
1015          */
1016         n = 0;
1017         for (;;) {
1018                 if ('0' <= *bp && *bp <= '9')
1019                         digit = *bp - '0';
1020                 else if ('a' <= *bp && *bp <= 'f')
1021                         digit = 10 + *bp - 'a';
1022                 else if ('A' <= *bp && *bp <= 'F')
1023                         digit = 10 + *bp - 'A';
1024                 else
1025                         break;
1026
1027                 if (digit >= base)
1028                         break;
1029
1030                 n = n * base + digit;
1031                 bp++;
1032         }
1033
1034         /*
1035          * Return value and success.
1036          */
1037         *num = n;
1038         return (0);
1039 }
1040
1041
1042 /*
1043  * Compare name field of record.
1044  */
1045 static int
1046 nfcmp(nf, rec)
1047         char *nf, *rec;
1048 {
1049         char *cp, tmp;
1050         int ret;
1051
1052         for (cp = rec; *cp != ':'; cp++)
1053                 ;
1054
1055         tmp = *(cp + 1);
1056         *(cp + 1) = '\0';
1057         ret = strcmp(nf, rec);
1058         *(cp + 1) = tmp;
1059
1060         return (ret);
1061 }