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