]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libc/gen/getcap.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.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;
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                                 cbuf = strdup(record);
255                                 if (capdbp->close(capdbp) < 0) {
256                                         free(cbuf);
257                                         return (-2);
258                                 }
259                                 if (cbuf == NULL) {
260                                         errno = ENOMEM;
261                                         return (-2);
262                                 }
263                                 *len = strlen(cbuf);
264                                 *cap = cbuf;
265                                 return (retval);
266                         } else {
267                                 fd = _open(*db_p, O_RDONLY, 0);
268                                 if (fd < 0)
269                                         continue;
270                                 myfd = 1;
271                         }
272                 }
273                 /*
274                  * Find the requested capability record ...
275                  */
276                 {
277                 char buf[BUFSIZ];
278                 char *b_end, *bp;
279                 int c;
280
281                 /*
282                  * Loop invariants:
283                  *      There is always room for one more character in record.
284                  *      R_end always points just past end of record.
285                  *      Rp always points just past last character in record.
286                  *      B_end always points just past last character in buf.
287                  *      Bp always points at next character in buf.
288                  */
289                 b_end = buf;
290                 bp = buf;
291                 for (;;) {
292
293                         /*
294                          * Read in a line implementing (\, newline)
295                          * line continuation.
296                          */
297                         rp = record;
298                         for (;;) {
299                                 if (bp >= b_end) {
300                                         int n;
301
302                                         n = _read(fd, buf, sizeof(buf));
303                                         if (n <= 0) {
304                                                 if (myfd)
305                                                         (void)_close(fd);
306                                                 if (n < 0) {
307                                                         free(record);
308                                                         return (-2);
309                                                 } else {
310                                                         fd = -1;
311                                                         eof = 1;
312                                                         break;
313                                                 }
314                                         }
315                                         b_end = buf+n;
316                                         bp = buf;
317                                 }
318
319                                 c = *bp++;
320                                 if (c == '\n') {
321                                         if (rp > record && *(rp-1) == '\\') {
322                                                 rp--;
323                                                 continue;
324                                         } else
325                                                 break;
326                                 }
327                                 *rp++ = c;
328
329                                 /*
330                                  * Enforce loop invariant: if no room
331                                  * left in record buffer, try to get
332                                  * some more.
333                                  */
334                                 if (rp >= r_end) {
335                                         u_int pos;
336                                         size_t newsize;
337
338                                         pos = rp - record;
339                                         newsize = r_end - record + BFRAG;
340                                         record = reallocf(record, newsize);
341                                         if (record == NULL) {
342                                                 errno = ENOMEM;
343                                                 if (myfd)
344                                                         (void)_close(fd);
345                                                 return (-2);
346                                         }
347                                         r_end = record + newsize;
348                                         rp = record + pos;
349                                 }
350                         }
351                                 /* loop invariant let's us do this */
352                         *rp++ = '\0';
353
354                         /*
355                          * If encountered eof check next file.
356                          */
357                         if (eof)
358                                 break;
359
360                         /*
361                          * Toss blank lines and comments.
362                          */
363                         if (*record == '\0' || *record == '#')
364                                 continue;
365
366                         /*
367                          * See if this is the record we want ...
368                          */
369                         if (cgetmatch(record, name) == 0) {
370                                 if (nfield == NULL || !nfcmp(nfield, record)) {
371                                         foundit = 1;
372                                         break;  /* found it! */
373                                 }
374                         }
375                 }
376         }
377                 if (foundit)
378                         break;
379         }
380
381         if (!foundit) {
382                 free(record);
383                 return (-1);
384         }
385
386         /*
387          * Got the capability record, but now we have to expand all tc=name
388          * references in it ...
389          */
390 tc_exp: {
391                 char *newicap, *s;
392                 int newilen;
393                 u_int ilen;
394                 int diff, iret, tclen;
395                 char *icap, *scan, *tc, *tcstart, *tcend;
396
397                 /*
398                  * Loop invariants:
399                  *      There is room for one more character in record.
400                  *      R_end points just past end of record.
401                  *      Rp points just past last character in record.
402                  *      Scan points at remainder of record that needs to be
403                  *      scanned for tc=name constructs.
404                  */
405                 scan = record;
406                 tc_not_resolved = 0;
407                 for (;;) {
408                         if ((tc = cgetcap(scan, "tc", '=')) == NULL)
409                                 break;
410
411                         /*
412                          * Find end of tc=name and stomp on the trailing `:'
413                          * (if present) so we can use it to call ourselves.
414                          */
415                         s = tc;
416                         for (;;)
417                                 if (*s == '\0')
418                                         break;
419                                 else
420                                         if (*s++ == ':') {
421                                                 *(s - 1) = '\0';
422                                                 break;
423                                         }
424                         tcstart = tc - 3;
425                         tclen = s - tcstart;
426                         tcend = s;
427
428                         iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
429                                       NULL);
430                         newicap = icap;         /* Put into a register. */
431                         newilen = ilen;
432                         if (iret != 0) {
433                                 /* an error */
434                                 if (iret < -1) {
435                                         if (myfd)
436                                                 (void)_close(fd);
437                                         free(record);
438                                         return (iret);
439                                 }
440                                 if (iret == 1)
441                                         tc_not_resolved = 1;
442                                 /* couldn't resolve tc */
443                                 if (iret == -1) {
444                                         *(s - 1) = ':';
445                                         scan = s - 1;
446                                         tc_not_resolved = 1;
447                                         continue;
448
449                                 }
450                         }
451                         /* not interested in name field of tc'ed record */
452                         s = newicap;
453                         for (;;)
454                                 if (*s == '\0')
455                                         break;
456                                 else
457                                         if (*s++ == ':')
458                                                 break;
459                         newilen -= s - newicap;
460                         newicap = s;
461
462                         /* make sure interpolated record is `:'-terminated */
463                         s += newilen;
464                         if (*(s-1) != ':') {
465                                 *s = ':';       /* overwrite NUL with : */
466                                 newilen++;
467                         }
468
469                         /*
470                          * Make sure there's enough room to insert the
471                          * new record.
472                          */
473                         diff = newilen - tclen;
474                         if (diff >= r_end - rp) {
475                                 u_int pos, tcpos, tcposend;
476                                 size_t newsize;
477
478                                 pos = rp - record;
479                                 newsize = r_end - record + diff + BFRAG;
480                                 tcpos = tcstart - record;
481                                 tcposend = tcend - record;
482                                 record = reallocf(record, newsize);
483                                 if (record == NULL) {
484                                         errno = ENOMEM;
485                                         if (myfd)
486                                                 (void)_close(fd);
487                                         free(icap);
488                                         return (-2);
489                                 }
490                                 r_end = record + newsize;
491                                 rp = record + pos;
492                                 tcstart = record + tcpos;
493                                 tcend = record + tcposend;
494                         }
495
496                         /*
497                          * Insert tc'ed record into our record.
498                          */
499                         s = tcstart + newilen;
500                         bcopy(tcend, s, rp - tcend);
501                         bcopy(newicap, tcstart, newilen);
502                         rp += diff;
503                         free(icap);
504
505                         /*
506                          * Start scan on `:' so next cgetcap works properly
507                          * (cgetcap always skips first field).
508                          */
509                         scan = s-1;
510                 }
511
512         }
513         /*
514          * Close file (if we opened it), give back any extra memory, and
515          * return capability, length and success.
516          */
517         if (myfd)
518                 (void)_close(fd);
519         *len = rp - record - 1; /* don't count NUL */
520         if (r_end > rp)
521                 if ((record =
522                      reallocf(record, (size_t)(rp - record))) == NULL) {
523                         errno = ENOMEM;
524                         return (-2);
525                 }
526
527         *cap = record;
528         if (tc_not_resolved)
529                 return (1);
530         return (0);
531 }
532
533 static int
534 cdbget(DB *capdbp, char **bp, const char *name)
535 {
536         DBT key, data;
537         char *namebuf;
538
539         namebuf = strdup(name);
540         if (namebuf == NULL)
541                 return (-2);
542         key.data = namebuf;
543         key.size = strlen(namebuf);
544
545         for (;;) {
546                 /* Get the reference. */
547                 switch(capdbp->get(capdbp, &key, &data, 0)) {
548                 case -1:
549                         free(namebuf);
550                         return (-2);
551                 case 1:
552                         free(namebuf);
553                         return (-1);
554                 }
555
556                 /* If not an index to another record, leave. */
557                 if (((char *)data.data)[0] != SHADOW)
558                         break;
559
560                 key.data = (char *)data.data + 1;
561                 key.size = data.size - 1;
562         }
563
564         *bp = (char *)data.data + 1;
565         free(namebuf);
566         return (((char *)(data.data))[0] == TCERR ? 1 : 0);
567 }
568
569 /*
570  * Cgetmatch will return 0 if name is one of the names of the capability
571  * record buf, -1 if not.
572  */
573 int
574 cgetmatch(const char *buf, const char *name)
575 {
576         const char *np, *bp;
577
578         if (name == NULL || *name == '\0')
579                 return -1;
580
581         /*
582          * Start search at beginning of record.
583          */
584         bp = buf;
585         for (;;) {
586                 /*
587                  * Try to match a record name.
588                  */
589                 np = name;
590                 for (;;)
591                         if (*np == '\0')
592                                 if (*bp == '|' || *bp == ':' || *bp == '\0')
593                                         return (0);
594                                 else
595                                         break;
596                         else
597                                 if (*bp++ != *np++)
598                                         break;
599
600                 /*
601                  * Match failed, skip to next name in record.
602                  */
603                 bp--;   /* a '|' or ':' may have stopped the match */
604                 for (;;)
605                         if (*bp == '\0' || *bp == ':')
606                                 return (-1);    /* match failed totally */
607                         else
608                                 if (*bp++ == '|')
609                                         break;  /* found next name */
610         }
611 }
612
613
614
615
616
617 int
618 cgetfirst(char **buf, char **db_array)
619 {
620         (void)cgetclose();
621         return (cgetnext(buf, db_array));
622 }
623
624 static FILE *pfp;
625 static int slash;
626 static char **dbp;
627
628 int
629 cgetclose(void)
630 {
631         if (pfp != NULL) {
632                 (void)fclose(pfp);
633                 pfp = NULL;
634         }
635         dbp = NULL;
636         gottoprec = 0;
637         slash = 0;
638         return(0);
639 }
640
641 /*
642  * Cgetnext() gets either the first or next entry in the logical database
643  * specified by db_array.  It returns 0 upon completion of the database, 1
644  * upon returning an entry with more remaining, and -1 if an error occurs.
645  */
646 int
647 cgetnext(char **bp, char **db_array)
648 {
649         size_t len;
650         int done, hadreaderr, savederrno, status;
651         char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
652         u_int dummy;
653
654         if (dbp == NULL)
655                 dbp = db_array;
656
657         if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
658                 (void)cgetclose();
659                 return (-1);
660         }
661         for (;;) {
662                 if (toprec && !gottoprec) {
663                         gottoprec = 1;
664                         line = toprec;
665                 } else {
666                         line = fgetln(pfp, &len);
667                         if (line == NULL && pfp) {
668                                 hadreaderr = ferror(pfp);
669                                 if (hadreaderr)
670                                         savederrno = errno;
671                                 fclose(pfp);
672                                 pfp = NULL;
673                                 if (hadreaderr) {
674                                         cgetclose();
675                                         errno = savederrno;
676                                         return (-1);
677                                 } else {
678                                         if (*++dbp == NULL) {
679                                                 (void)cgetclose();
680                                                 return (0);
681                                         } else if ((pfp =
682                                             fopen(*dbp, "r")) == NULL) {
683                                                 (void)cgetclose();
684                                                 return (-1);
685                                         } else
686                                                 continue;
687                                 }
688                         } else
689                                 line[len - 1] = '\0';
690                         if (len == 1) {
691                                 slash = 0;
692                                 continue;
693                         }
694                         if (isspace((unsigned char)*line) ||
695                             *line == ':' || *line == '#' || slash) {
696                                 if (line[len - 2] == '\\')
697                                         slash = 1;
698                                 else
699                                         slash = 0;
700                                 continue;
701                         }
702                         if (line[len - 2] == '\\')
703                                 slash = 1;
704                         else
705                                 slash = 0;
706                 }
707
708
709                 /*
710                  * Line points to a name line.
711                  */
712                 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(char *buf, const char *cap, char **str)
785 {
786         u_int m_room;
787         char *bp, *mp;
788         int len;
789         char *mem;
790
791         /*
792          * Find string capability cap
793          */
794         bp = cgetcap(buf, cap, '=');
795         if (bp == NULL)
796                 return (-1);
797
798         /*
799          * Conversion / storage allocation loop ...  Allocate memory in
800          * chunks SFRAG in size.
801          */
802         if ((mem = malloc(SFRAG)) == NULL) {
803                 errno = ENOMEM;
804                 return (-2);    /* couldn't even allocate the first fragment */
805         }
806         m_room = SFRAG;
807         mp = mem;
808
809         while (*bp != ':' && *bp != '\0') {
810                 /*
811                  * Loop invariants:
812                  *      There is always room for one more character in mem.
813                  *      Mp always points just past last character in mem.
814                  *      Bp always points at next character in buf.
815                  */
816                 if (*bp == '^') {
817                         bp++;
818                         if (*bp == ':' || *bp == '\0')
819                                 break;  /* drop unfinished escape */
820                         if (*bp == '?') {
821                                 *mp++ = '\177';
822                                 bp++;
823                         } else
824                                 *mp++ = *bp++ & 037;
825                 } else if (*bp == '\\') {
826                         bp++;
827                         if (*bp == ':' || *bp == '\0')
828                                 break;  /* drop unfinished escape */
829                         if ('0' <= *bp && *bp <= '7') {
830                                 int n, i;
831
832                                 n = 0;
833                                 i = 3;  /* maximum of three octal digits */
834                                 do {
835                                         n = n * 8 + (*bp++ - '0');
836                                 } while (--i && '0' <= *bp && *bp <= '7');
837                                 *mp++ = n;
838                         }
839                         else switch (*bp++) {
840                                 case 'b': case 'B':
841                                         *mp++ = '\b';
842                                         break;
843                                 case 't': case 'T':
844                                         *mp++ = '\t';
845                                         break;
846                                 case 'n': case 'N':
847                                         *mp++ = '\n';
848                                         break;
849                                 case 'f': case 'F':
850                                         *mp++ = '\f';
851                                         break;
852                                 case 'r': case 'R':
853                                         *mp++ = '\r';
854                                         break;
855                                 case 'e': case 'E':
856                                         *mp++ = ESC;
857                                         break;
858                                 case 'c': case 'C':
859                                         *mp++ = ':';
860                                         break;
861                                 default:
862                                         /*
863                                          * Catches '\', '^', and
864                                          *  everything else.
865                                          */
866                                         *mp++ = *(bp-1);
867                                         break;
868                         }
869                 } else
870                         *mp++ = *bp++;
871                 m_room--;
872
873                 /*
874                  * Enforce loop invariant: if no room left in current
875                  * buffer, try to get some more.
876                  */
877                 if (m_room == 0) {
878                         size_t size = mp - mem;
879
880                         if ((mem = reallocf(mem, size + SFRAG)) == NULL)
881                                 return (-2);
882                         m_room = SFRAG;
883                         mp = mem + size;
884                 }
885         }
886         *mp++ = '\0';   /* loop invariant let's us do this */
887         m_room--;
888         len = mp - mem - 1;
889
890         /*
891          * Give back any extra memory and return value and success.
892          */
893         if (m_room != 0)
894                 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
895                         return (-2);
896         *str = mem;
897         return (len);
898 }
899
900 /*
901  * Cgetustr retrieves the value of the string capability cap from the
902  * capability record pointed to by buf.  The difference between cgetustr()
903  * and cgetstr() is that cgetustr does not decode escapes but rather treats
904  * all characters literally.  A pointer to a  NUL terminated malloc'd
905  * copy of the string is returned in the char pointed to by str.  The
906  * length of the string not including the trailing NUL is returned on success,
907  * -1 if the requested string capability couldn't be found, -2 if a system
908  * error was encountered (storage allocation failure).
909  */
910 int
911 cgetustr(char *buf, const char *cap, char **str)
912 {
913         u_int m_room;
914         char *bp, *mp;
915         int len;
916         char *mem;
917
918         /*
919          * Find string capability cap
920          */
921         if ((bp = cgetcap(buf, cap, '=')) == NULL)
922                 return (-1);
923
924         /*
925          * Conversion / storage allocation loop ...  Allocate memory in
926          * chunks SFRAG in size.
927          */
928         if ((mem = malloc(SFRAG)) == NULL) {
929                 errno = ENOMEM;
930                 return (-2);    /* couldn't even allocate the first fragment */
931         }
932         m_room = SFRAG;
933         mp = mem;
934
935         while (*bp != ':' && *bp != '\0') {
936                 /*
937                  * Loop invariants:
938                  *      There is always room for one more character in mem.
939                  *      Mp always points just past last character in mem.
940                  *      Bp always points at next character in buf.
941                  */
942                 *mp++ = *bp++;
943                 m_room--;
944
945                 /*
946                  * Enforce loop invariant: if no room left in current
947                  * buffer, try to get some more.
948                  */
949                 if (m_room == 0) {
950                         size_t size = mp - mem;
951
952                         if ((mem = reallocf(mem, size + SFRAG)) == NULL)
953                                 return (-2);
954                         m_room = SFRAG;
955                         mp = mem + size;
956                 }
957         }
958         *mp++ = '\0';   /* loop invariant let's us do this */
959         m_room--;
960         len = mp - mem - 1;
961
962         /*
963          * Give back any extra memory and return value and success.
964          */
965         if (m_room != 0)
966                 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
967                         return (-2);
968         *str = mem;
969         return (len);
970 }
971
972 /*
973  * Cgetnum retrieves the value of the numeric capability cap from the
974  * capability record pointed to by buf.  The numeric value is returned in
975  * the long pointed to by num.  0 is returned on success, -1 if the requested
976  * numeric capability couldn't be found.
977  */
978 int
979 cgetnum(char *buf, const char *cap, long *num)
980 {
981         long n;
982         int base, digit;
983         char *bp;
984
985         /*
986          * Find numeric capability cap
987          */
988         bp = cgetcap(buf, cap, '#');
989         if (bp == NULL)
990                 return (-1);
991
992         /*
993          * Look at value and determine numeric base:
994          *      0x... or 0X...  hexadecimal,
995          * else 0...            octal,
996          * else                 decimal.
997          */
998         if (*bp == '0') {
999                 bp++;
1000                 if (*bp == 'x' || *bp == 'X') {
1001                         bp++;
1002                         base = 16;
1003                 } else
1004                         base = 8;
1005         } else
1006                 base = 10;
1007
1008         /*
1009          * Conversion loop ...
1010          */
1011         n = 0;
1012         for (;;) {
1013                 if ('0' <= *bp && *bp <= '9')
1014                         digit = *bp - '0';
1015                 else if ('a' <= *bp && *bp <= 'f')
1016                         digit = 10 + *bp - 'a';
1017                 else if ('A' <= *bp && *bp <= 'F')
1018                         digit = 10 + *bp - 'A';
1019                 else
1020                         break;
1021
1022                 if (digit >= base)
1023                         break;
1024
1025                 n = n * base + digit;
1026                 bp++;
1027         }
1028
1029         /*
1030          * Return value and success.
1031          */
1032         *num = n;
1033         return (0);
1034 }
1035
1036
1037 /*
1038  * Compare name field of record.
1039  */
1040 static int
1041 nfcmp(char *nf, char *rec)
1042 {
1043         char *cp, tmp;
1044         int ret;
1045
1046         for (cp = rec; *cp != ':'; cp++)
1047                 ;
1048
1049         tmp = *(cp + 1);
1050         *(cp + 1) = '\0';
1051         ret = strcmp(nf, rec);
1052         *(cp + 1) = tmp;
1053
1054         return (ret);
1055 }