]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/gen/getcap.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.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
37 #if defined(LIBC_SCCS) && !defined(lint)
38 static char sccsid[] = "@(#)getcap.c    8.3 (Berkeley) 3/25/94";
39 #endif /* LIBC_SCCS and not lint */
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42
43 #include "namespace.h"
44 #include <sys/types.h>
45
46 #include <ctype.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 #include "un-namespace.h"
55
56 #include <db.h>
57
58 #define BFRAG           1024
59 #define BSIZE           1024
60 #define ESC             ('[' & 037)     /* ASCII ESC */
61 #define MAX_RECURSION   32              /* maximum getent recursion */
62 #define SFRAG           100             /* cgetstr mallocs in SFRAG chunks */
63
64 #define RECOK   (char)0
65 #define TCERR   (char)1
66 #define SHADOW  (char)2
67
68 static size_t    topreclen;     /* toprec length */
69 static char     *toprec;        /* Additional record specified by cgetset() */
70 static int       gottoprec;     /* Flag indicating retrieval of toprecord */
71
72 static int      cdbget(DB *, char **, const char *);
73 static int      getent(char **, u_int *, char **, int, const char *, int, char *);
74 static int      nfcmp(char *, char *);
75
76 /*
77  * Cgetset() allows the addition of a user specified buffer to be added
78  * to the database array, in effect "pushing" the buffer on top of the
79  * virtual database. 0 is returned on success, -1 on failure.
80  */
81 int
82 cgetset(const 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(char *buf, const char *cap, int type)
115 {
116         char *bp;
117         const char *cp;
118
119         bp = buf;
120         for (;;) {
121                 /*
122                  * Skip past the current capability field - it's either the
123                  * name field if this is the first time through the loop, or
124                  * the remainder of a field whose name failed to match cap.
125                  */
126                 for (;;)
127                         if (*bp == '\0')
128                                 return (NULL);
129                         else
130                                 if (*bp++ == ':')
131                                         break;
132
133                 /*
134                  * Try to match (cap, type) in buf.
135                  */
136                 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
137                         continue;
138                 if (*cp != '\0')
139                         continue;
140                 if (*bp == '@')
141                         return (NULL);
142                 if (type == ':') {
143                         if (*bp != '\0' && *bp != ':')
144                                 continue;
145                         return(bp);
146                 }
147                 if (*bp != type)
148                         continue;
149                 bp++;
150                 return (*bp == '@' ? NULL : bp);
151         }
152         /* NOTREACHED */
153 }
154
155 /*
156  * Cgetent extracts the capability record name from the NULL terminated file
157  * array db_array and returns a pointer to a malloc'd copy of it in buf.
158  * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
159  * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
160  * -1 if the requested record couldn't be found, -2 if a system error was
161  * encountered (couldn't open/read a file, etc.), and -3 if a potential
162  * reference loop is detected.
163  */
164 int
165 cgetent(char **buf, char **db_array, const char *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(char **cap, u_int *len, char **db_array, int fd, const char *name,
192     int depth, char *nfield)
193 {
194         DB *capdbp;
195         char *r_end, *rp, **db_p;
196         int myfd, eof, foundit, retval, clen;
197         char *record, *cbuf;
198         int tc_not_resolved;
199         char pbuf[_POSIX_PATH_MAX];
200
201         /*
202          * Return with ``loop detected'' error if we've recursed more than
203          * MAX_RECURSION times.
204          */
205         if (depth > MAX_RECURSION)
206                 return (-3);
207
208         /*
209          * Check if we have a top record from cgetset().
210          */
211         if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
212                 if ((record = malloc (topreclen + BFRAG)) == NULL) {
213                         errno = ENOMEM;
214                         return (-2);
215                 }
216                 (void)strcpy(record, toprec);
217                 myfd = 0;
218                 db_p = db_array;
219                 rp = record + topreclen + 1;
220                 r_end = rp + BFRAG;
221                 goto tc_exp;
222         }
223         /*
224          * Allocate first chunk of memory.
225          */
226         if ((record = malloc(BFRAG)) == NULL) {
227                 errno = ENOMEM;
228                 return (-2);
229         }
230         r_end = record + BFRAG;
231         foundit = 0;
232         /*
233          * Loop through database array until finding the record.
234          */
235
236         for (db_p = db_array; *db_p != NULL; db_p++) {
237                 eof = 0;
238
239                 /*
240                  * Open database if not already open.
241                  */
242
243                 if (fd >= 0) {
244                         (void)lseek(fd, (off_t)0, SEEK_SET);
245                         myfd = 0;
246                 } else {
247                         (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
248                         if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
249                              != NULL) {
250                                 free(record);
251                                 retval = cdbget(capdbp, &record, name);
252                                 if (retval < 0) {
253                                         /* no record available */
254                                         (void)capdbp->close(capdbp);
255                                         return (retval);
256                                 }
257                                 /* save the data; close frees it */
258                                 clen = strlen(record);
259                                 cbuf = malloc(clen + 1);
260                                 memcpy(cbuf, record, clen + 1);
261                                 if (capdbp->close(capdbp) < 0) {
262                                         free(cbuf);
263                                         return (-2);
264                                 }
265                                 *len = clen;
266                                 *cap = cbuf;
267                                 return (retval);
268                         } else {
269                                 fd = _open(*db_p, O_RDONLY, 0);
270                                 if (fd < 0)
271                                         continue;
272                                 myfd = 1;
273                         }
274                 }
275                 /*
276                  * Find the requested capability record ...
277                  */
278                 {
279                 char buf[BUFSIZ];
280                 char *b_end, *bp;
281                 int c;
282
283                 /*
284                  * Loop invariants:
285                  *      There is always room for one more character in record.
286                  *      R_end always points just past end of record.
287                  *      Rp always points just past last character in record.
288                  *      B_end always points just past last character in buf.
289                  *      Bp always points at next character in buf.
290                  */
291                 b_end = buf;
292                 bp = buf;
293                 for (;;) {
294
295                         /*
296                          * Read in a line implementing (\, newline)
297                          * line continuation.
298                          */
299                         rp = record;
300                         for (;;) {
301                                 if (bp >= b_end) {
302                                         int n;
303
304                                         n = _read(fd, buf, sizeof(buf));
305                                         if (n <= 0) {
306                                                 if (myfd)
307                                                         (void)_close(fd);
308                                                 if (n < 0) {
309                                                         free(record);
310                                                         return (-2);
311                                                 } else {
312                                                         fd = -1;
313                                                         eof = 1;
314                                                         break;
315                                                 }
316                                         }
317                                         b_end = buf+n;
318                                         bp = buf;
319                                 }
320
321                                 c = *bp++;
322                                 if (c == '\n') {
323                                         if (rp > record && *(rp-1) == '\\') {
324                                                 rp--;
325                                                 continue;
326                                         } else
327                                                 break;
328                                 }
329                                 *rp++ = c;
330
331                                 /*
332                                  * Enforce loop invariant: if no room
333                                  * left in record buffer, try to get
334                                  * some more.
335                                  */
336                                 if (rp >= r_end) {
337                                         u_int pos;
338                                         size_t newsize;
339
340                                         pos = rp - record;
341                                         newsize = r_end - record + BFRAG;
342                                         record = reallocf(record, newsize);
343                                         if (record == NULL) {
344                                                 errno = ENOMEM;
345                                                 if (myfd)
346                                                         (void)_close(fd);
347                                                 return (-2);
348                                         }
349                                         r_end = record + newsize;
350                                         rp = record + pos;
351                                 }
352                         }
353                                 /* loop invariant let's us do this */
354                         *rp++ = '\0';
355
356                         /*
357                          * If encountered eof check next file.
358                          */
359                         if (eof)
360                                 break;
361
362                         /*
363                          * Toss blank lines and comments.
364                          */
365                         if (*record == '\0' || *record == '#')
366                                 continue;
367
368                         /*
369                          * See if this is the record we want ...
370                          */
371                         if (cgetmatch(record, name) == 0) {
372                                 if (nfield == NULL || !nfcmp(nfield, record)) {
373                                         foundit = 1;
374                                         break;  /* found it! */
375                                 }
376                         }
377                 }
378         }
379                 if (foundit)
380                         break;
381         }
382
383         if (!foundit) {
384                 free(record);
385                 return (-1);
386         }
387
388         /*
389          * Got the capability record, but now we have to expand all tc=name
390          * references in it ...
391          */
392 tc_exp: {
393                 char *newicap, *s;
394                 int newilen;
395                 u_int ilen;
396                 int diff, iret, tclen;
397                 char *icap, *scan, *tc, *tcstart, *tcend;
398
399                 /*
400                  * Loop invariants:
401                  *      There is room for one more character in record.
402                  *      R_end points just past end of record.
403                  *      Rp points just past last character in record.
404                  *      Scan points at remainder of record that needs to be
405                  *      scanned for tc=name constructs.
406                  */
407                 scan = record;
408                 tc_not_resolved = 0;
409                 for (;;) {
410                         if ((tc = cgetcap(scan, "tc", '=')) == NULL)
411                                 break;
412
413                         /*
414                          * Find end of tc=name and stomp on the trailing `:'
415                          * (if present) so we can use it to call ourselves.
416                          */
417                         s = tc;
418                         for (;;)
419                                 if (*s == '\0')
420                                         break;
421                                 else
422                                         if (*s++ == ':') {
423                                                 *(s - 1) = '\0';
424                                                 break;
425                                         }
426                         tcstart = tc - 3;
427                         tclen = s - tcstart;
428                         tcend = s;
429
430                         iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
431                                       NULL);
432                         newicap = icap;         /* Put into a register. */
433                         newilen = ilen;
434                         if (iret != 0) {
435                                 /* an error */
436                                 if (iret < -1) {
437                                         if (myfd)
438                                                 (void)_close(fd);
439                                         free(record);
440                                         return (iret);
441                                 }
442                                 if (iret == 1)
443                                         tc_not_resolved = 1;
444                                 /* couldn't resolve tc */
445                                 if (iret == -1) {
446                                         *(s - 1) = ':';
447                                         scan = s - 1;
448                                         tc_not_resolved = 1;
449                                         continue;
450
451                                 }
452                         }
453                         /* not interested in name field of tc'ed record */
454                         s = newicap;
455                         for (;;)
456                                 if (*s == '\0')
457                                         break;
458                                 else
459                                         if (*s++ == ':')
460                                                 break;
461                         newilen -= s - newicap;
462                         newicap = s;
463
464                         /* make sure interpolated record is `:'-terminated */
465                         s += newilen;
466                         if (*(s-1) != ':') {
467                                 *s = ':';       /* overwrite NUL with : */
468                                 newilen++;
469                         }
470
471                         /*
472                          * Make sure there's enough room to insert the
473                          * new record.
474                          */
475                         diff = newilen - tclen;
476                         if (diff >= r_end - rp) {
477                                 u_int pos, tcpos, tcposend;
478                                 size_t newsize;
479
480                                 pos = rp - record;
481                                 newsize = r_end - record + diff + BFRAG;
482                                 tcpos = tcstart - record;
483                                 tcposend = tcend - record;
484                                 record = reallocf(record, newsize);
485                                 if (record == NULL) {
486                                         errno = ENOMEM;
487                                         if (myfd)
488                                                 (void)_close(fd);
489                                         free(icap);
490                                         return (-2);
491                                 }
492                                 r_end = record + newsize;
493                                 rp = record + pos;
494                                 tcstart = record + tcpos;
495                                 tcend = record + tcposend;
496                         }
497
498                         /*
499                          * Insert tc'ed record into our record.
500                          */
501                         s = tcstart + newilen;
502                         bcopy(tcend, s, rp - tcend);
503                         bcopy(newicap, tcstart, newilen);
504                         rp += diff;
505                         free(icap);
506
507                         /*
508                          * Start scan on `:' so next cgetcap works properly
509                          * (cgetcap always skips first field).
510                          */
511                         scan = s-1;
512                 }
513
514         }
515         /*
516          * Close file (if we opened it), give back any extra memory, and
517          * return capability, length and success.
518          */
519         if (myfd)
520                 (void)_close(fd);
521         *len = rp - record - 1; /* don't count NUL */
522         if (r_end > rp)
523                 if ((record =
524                      reallocf(record, (size_t)(rp - record))) == NULL) {
525                         errno = ENOMEM;
526                         return (-2);
527                 }
528
529         *cap = record;
530         if (tc_not_resolved)
531                 return (1);
532         return (0);
533 }
534
535 static int
536 cdbget(DB *capdbp, char **bp, const char *name)
537 {
538         DBT key, data;
539         char *namebuf;
540
541         namebuf = strdup(name);
542         if (namebuf == NULL)
543                 return (-2);
544         key.data = namebuf;
545         key.size = strlen(namebuf);
546
547         for (;;) {
548                 /* Get the reference. */
549                 switch(capdbp->get(capdbp, &key, &data, 0)) {
550                 case -1:
551                         free(namebuf);
552                         return (-2);
553                 case 1:
554                         free(namebuf);
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         free(namebuf);
568         return (((char *)(data.data))[0] == TCERR ? 1 : 0);
569 }
570
571 /*
572  * Cgetmatch will return 0 if name is one of the names of the capability
573  * record buf, -1 if not.
574  */
575 int
576 cgetmatch(const char *buf, const char *name)
577 {
578         const char *np, *bp;
579
580         if (name == NULL || *name == '\0')
581                 return -1;
582
583         /*
584          * Start search at beginning of record.
585          */
586         bp = buf;
587         for (;;) {
588                 /*
589                  * Try to match a record name.
590                  */
591                 np = name;
592                 for (;;)
593                         if (*np == '\0')
594                                 if (*bp == '|' || *bp == ':' || *bp == '\0')
595                                         return (0);
596                                 else
597                                         break;
598                         else
599                                 if (*bp++ != *np++)
600                                         break;
601
602                 /*
603                  * Match failed, skip to next name in record.
604                  */
605                 bp--;   /* a '|' or ':' may have stopped the match */
606                 for (;;)
607                         if (*bp == '\0' || *bp == ':')
608                                 return (-1);    /* match failed totally */
609                         else
610                                 if (*bp++ == '|')
611                                         break;  /* found next name */
612         }
613 }
614
615
616
617
618
619 int
620 cgetfirst(char **buf, char **db_array)
621 {
622         (void)cgetclose();
623         return (cgetnext(buf, db_array));
624 }
625
626 static FILE *pfp;
627 static int slash;
628 static char **dbp;
629
630 int
631 cgetclose(void)
632 {
633         if (pfp != NULL) {
634                 (void)fclose(pfp);
635                 pfp = NULL;
636         }
637         dbp = NULL;
638         gottoprec = 0;
639         slash = 0;
640         return(0);
641 }
642
643 /*
644  * Cgetnext() gets either the first or next entry in the logical database
645  * specified by db_array.  It returns 0 upon completion of the database, 1
646  * upon returning an entry with more remaining, and -1 if an error occurs.
647  */
648 int
649 cgetnext(char **bp, 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(char *buf, const char *cap, char **str)
788 {
789         u_int m_room;
790         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                                 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(char *buf, const char *cap, char **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(char *buf, const char *cap, long *num)
983 {
984         long n;
985         int base, digit;
986         char *bp;
987
988         /*
989          * Find numeric capability cap
990          */
991         bp = cgetcap(buf, cap, '#');
992         if (bp == NULL)
993                 return (-1);
994
995         /*
996          * Look at value and determine numeric base:
997          *      0x... or 0X...  hexadecimal,
998          * else 0...            octal,
999          * else                 decimal.
1000          */
1001         if (*bp == '0') {
1002                 bp++;
1003                 if (*bp == 'x' || *bp == 'X') {
1004                         bp++;
1005                         base = 16;
1006                 } else
1007                         base = 8;
1008         } else
1009                 base = 10;
1010
1011         /*
1012          * Conversion loop ...
1013          */
1014         n = 0;
1015         for (;;) {
1016                 if ('0' <= *bp && *bp <= '9')
1017                         digit = *bp - '0';
1018                 else if ('a' <= *bp && *bp <= 'f')
1019                         digit = 10 + *bp - 'a';
1020                 else if ('A' <= *bp && *bp <= 'F')
1021                         digit = 10 + *bp - 'A';
1022                 else
1023                         break;
1024
1025                 if (digit >= base)
1026                         break;
1027
1028                 n = n * base + digit;
1029                 bp++;
1030         }
1031
1032         /*
1033          * Return value and success.
1034          */
1035         *num = n;
1036         return (0);
1037 }
1038
1039
1040 /*
1041  * Compare name field of record.
1042  */
1043 static int
1044 nfcmp(char *nf, char *rec)
1045 {
1046         char *cp, tmp;
1047         int ret;
1048
1049         for (cp = rec; *cp != ':'; cp++)
1050                 ;
1051
1052         tmp = *(cp + 1);
1053         *(cp + 1) = '\0';
1054         ret = strcmp(nf, rec);
1055         *(cp + 1) = tmp;
1056
1057         return (ret);
1058 }