]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/gen/getcap.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ trunk r321545,
[FreeBSD/FreeBSD.git] / lib / libc / gen / getcap.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1992, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Casey Leedom of Lawrence Livermore National Laboratory.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #if defined(LIBC_SCCS) && !defined(lint)
36 static char sccsid[] = "@(#)getcap.c    8.3 (Berkeley) 3/25/94";
37 #endif /* LIBC_SCCS and not lint */
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40
41 #include "namespace.h"
42 #include <sys/types.h>
43
44 #include <ctype.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include "un-namespace.h"
53
54 #include <db.h>
55
56 #define BFRAG           1024
57 #define BSIZE           1024
58 #define ESC             ('[' & 037)     /* ASCII ESC */
59 #define MAX_RECURSION   32              /* maximum getent recursion */
60 #define SFRAG           100             /* cgetstr mallocs in SFRAG chunks */
61
62 #define RECOK   (char)0
63 #define TCERR   (char)1
64 #define SHADOW  (char)2
65
66 static size_t    topreclen;     /* toprec length */
67 static char     *toprec;        /* Additional record specified by cgetset() */
68 static int       gottoprec;     /* Flag indicating retrieval of toprecord */
69
70 static int      cdbget(DB *, char **, const char *);
71 static int      getent(char **, u_int *, char **, int, const char *, int, char *);
72 static int      nfcmp(char *, char *);
73
74 /*
75  * Cgetset() allows the addition of a user specified buffer to be added
76  * to the database array, in effect "pushing" the buffer on top of the
77  * virtual database. 0 is returned on success, -1 on failure.
78  */
79 int
80 cgetset(const char *ent)
81 {
82         if (ent == NULL) {
83                 if (toprec)
84                         free(toprec);
85                 toprec = NULL;
86                 topreclen = 0;
87                 return (0);
88         }
89         topreclen = strlen(ent);
90         if ((toprec = malloc (topreclen + 1)) == NULL) {
91                 errno = ENOMEM;
92                 return (-1);
93         }
94         gottoprec = 0;
95         (void)strcpy(toprec, ent);
96         return (0);
97 }
98
99 /*
100  * Cgetcap searches the capability record buf for the capability cap with
101  * type `type'.  A pointer to the value of cap is returned on success, NULL
102  * if the requested capability couldn't be found.
103  *
104  * Specifying a type of ':' means that nothing should follow cap (:cap:).
105  * In this case a pointer to the terminating ':' or NUL will be returned if
106  * cap is found.
107  *
108  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
109  * return NULL.
110  */
111 char *
112 cgetcap(char *buf, const char *cap, int type)
113 {
114         char *bp;
115         const char *cp;
116
117         bp = buf;
118         for (;;) {
119                 /*
120                  * Skip past the current capability field - it's either the
121                  * name field if this is the first time through the loop, or
122                  * the remainder of a field whose name failed to match cap.
123                  */
124                 for (;;)
125                         if (*bp == '\0')
126                                 return (NULL);
127                         else
128                                 if (*bp++ == ':')
129                                         break;
130
131                 /*
132                  * Try to match (cap, type) in buf.
133                  */
134                 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
135                         continue;
136                 if (*cp != '\0')
137                         continue;
138                 if (*bp == '@')
139                         return (NULL);
140                 if (type == ':') {
141                         if (*bp != '\0' && *bp != ':')
142                                 continue;
143                         return(bp);
144                 }
145                 if (*bp != type)
146                         continue;
147                 bp++;
148                 return (*bp == '@' ? NULL : bp);
149         }
150         /* NOTREACHED */
151 }
152
153 /*
154  * Cgetent extracts the capability record name from the NULL terminated file
155  * array db_array and returns a pointer to a malloc'd copy of it in buf.
156  * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
157  * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
158  * -1 if the requested record couldn't be found, -2 if a system error was
159  * encountered (couldn't open/read a file, etc.), and -3 if a potential
160  * reference loop is detected.
161  */
162 int
163 cgetent(char **buf, char **db_array, const char *name)
164 {
165         u_int dummy;
166
167         return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
168 }
169
170 /*
171  * Getent implements the functions of cgetent.  If fd is non-negative,
172  * *db_array has already been opened and fd is the open file descriptor.  We
173  * do this to save time and avoid using up file descriptors for tc=
174  * recursions.
175  *
176  * Getent returns the same success/failure codes as cgetent.  On success, a
177  * pointer to a malloc'ed capability record with all tc= capabilities fully
178  * expanded and its length (not including trailing ASCII NUL) are left in
179  * *cap and *len.
180  *
181  * Basic algorithm:
182  *      + Allocate memory incrementally as needed in chunks of size BFRAG
183  *        for capability buffer.
184  *      + Recurse for each tc=name and interpolate result.  Stop when all
185  *        names interpolated, a name can't be found, or depth exceeds
186  *        MAX_RECURSION.
187  */
188 static int
189 getent(char **cap, u_int *len, char **db_array, int fd, const char *name,
190     int depth, char *nfield)
191 {
192         DB *capdbp;
193         char *r_end, *rp, **db_p;
194         int myfd, eof, foundit, retval;
195         char *record, *cbuf;
196         int tc_not_resolved;
197         char pbuf[_POSIX_PATH_MAX];
198
199         /*
200          * Return with ``loop detected'' error if we've recursed more than
201          * MAX_RECURSION times.
202          */
203         if (depth > MAX_RECURSION)
204                 return (-3);
205
206         /*
207          * Check if we have a top record from cgetset().
208          */
209         if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
210                 if ((record = malloc (topreclen + BFRAG)) == NULL) {
211                         errno = ENOMEM;
212                         return (-2);
213                 }
214                 (void)strcpy(record, toprec);
215                 myfd = 0;
216                 db_p = db_array;
217                 rp = record + topreclen + 1;
218                 r_end = rp + BFRAG;
219                 goto tc_exp;
220         }
221         /*
222          * Allocate first chunk of memory.
223          */
224         if ((record = malloc(BFRAG)) == NULL) {
225                 errno = ENOMEM;
226                 return (-2);
227         }
228         r_end = record + BFRAG;
229         foundit = 0;
230         /*
231          * Loop through database array until finding the record.
232          */
233
234         for (db_p = db_array; *db_p != NULL; db_p++) {
235                 eof = 0;
236
237                 /*
238                  * Open database if not already open.
239                  */
240
241                 if (fd >= 0) {
242                         (void)lseek(fd, (off_t)0, SEEK_SET);
243                         myfd = 0;
244                 } else {
245                         (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
246                         if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
247                              != NULL) {
248                                 free(record);
249                                 retval = cdbget(capdbp, &record, name);
250                                 if (retval < 0) {
251                                         /* no record available */
252                                         (void)capdbp->close(capdbp);
253                                         return (retval);
254                                 }
255                                 /* save the data; close frees it */
256                                 cbuf = strdup(record);
257                                 if (capdbp->close(capdbp) < 0) {
258                                         free(cbuf);
259                                         return (-2);
260                                 }
261                                 if (cbuf == NULL) {
262                                         errno = ENOMEM;
263                                         return (-2);
264                                 }
265                                 *len = strlen(cbuf);
266                                 *cap = cbuf;
267                                 return (retval);
268                         } else {
269                                 fd = _open(*db_p, O_RDONLY | O_CLOEXEC, 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, 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, "re")) == 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, "re")) == 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                 done = 0;
715                 np = nbuf;
716                 for (;;) {
717                         for (cp = line; *cp != '\0'; cp++) {
718                                 if (*cp == ':') {
719                                         *np++ = ':';
720                                         done = 1;
721                                         break;
722                                 }
723                                 if (*cp == '\\')
724                                         break;
725                                 *np++ = *cp;
726                         }
727                         if (done) {
728                                 *np = '\0';
729                                 break;
730                         } else { /* name field extends beyond the line */
731                                 line = fgetln(pfp, &len);
732                                 if (line == NULL && pfp) {
733                                         /* Name extends beyond the EOF! */
734                                         hadreaderr = ferror(pfp);
735                                         if (hadreaderr)
736                                                 savederrno = errno;
737                                         fclose(pfp);
738                                         pfp = NULL;
739                                         if (hadreaderr) {
740                                                 cgetclose();
741                                                 errno = savederrno;
742                                                 return (-1);
743                                         } else {
744                                                 cgetclose();
745                                                 return (-1);
746                                         }
747                                 } else
748                                         line[len - 1] = '\0';
749                         }
750                 }
751                 rp = buf;
752                 for(cp = nbuf; *cp != '\0'; cp++)
753                         if (*cp == '|' || *cp == ':')
754                                 break;
755                         else
756                                 *rp++ = *cp;
757
758                 *rp = '\0';
759                 /*
760                  * XXX
761                  * Last argument of getent here should be nbuf if we want true
762                  * sequential access in the case of duplicates.
763                  * With NULL, getent will return the first entry found
764                  * rather than the duplicate entry record.  This is a
765                  * matter of semantics that should be resolved.
766                  */
767                 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
768                 if (status == -2 || status == -3)
769                         (void)cgetclose();
770
771                 return (status + 1);
772         }
773         /* NOTREACHED */
774 }
775
776 /*
777  * Cgetstr retrieves the value of the string capability cap from the
778  * capability record pointed to by buf.  A pointer to a decoded, NUL
779  * terminated, malloc'd copy of the string is returned in the char *
780  * pointed to by str.  The length of the string not including the trailing
781  * NUL is returned on success, -1 if the requested string capability
782  * couldn't be found, -2 if a system error was encountered (storage
783  * allocation failure).
784  */
785 int
786 cgetstr(char *buf, const char *cap, char **str)
787 {
788         u_int m_room;
789         char *bp, *mp;
790         int len;
791         char *mem;
792
793         /*
794          * Find string capability cap
795          */
796         bp = cgetcap(buf, cap, '=');
797         if (bp == NULL)
798                 return (-1);
799
800         /*
801          * Conversion / storage allocation loop ...  Allocate memory in
802          * chunks SFRAG in size.
803          */
804         if ((mem = malloc(SFRAG)) == NULL) {
805                 errno = ENOMEM;
806                 return (-2);    /* couldn't even allocate the first fragment */
807         }
808         m_room = SFRAG;
809         mp = mem;
810
811         while (*bp != ':' && *bp != '\0') {
812                 /*
813                  * Loop invariants:
814                  *      There is always room for one more character in mem.
815                  *      Mp always points just past last character in mem.
816                  *      Bp always points at next character in buf.
817                  */
818                 if (*bp == '^') {
819                         bp++;
820                         if (*bp == ':' || *bp == '\0')
821                                 break;  /* drop unfinished escape */
822                         if (*bp == '?') {
823                                 *mp++ = '\177';
824                                 bp++;
825                         } else
826                                 *mp++ = *bp++ & 037;
827                 } else if (*bp == '\\') {
828                         bp++;
829                         if (*bp == ':' || *bp == '\0')
830                                 break;  /* drop unfinished escape */
831                         if ('0' <= *bp && *bp <= '7') {
832                                 int n, i;
833
834                                 n = 0;
835                                 i = 3;  /* maximum of three octal digits */
836                                 do {
837                                         n = n * 8 + (*bp++ - '0');
838                                 } while (--i && '0' <= *bp && *bp <= '7');
839                                 *mp++ = n;
840                         }
841                         else switch (*bp++) {
842                                 case 'b': case 'B':
843                                         *mp++ = '\b';
844                                         break;
845                                 case 't': case 'T':
846                                         *mp++ = '\t';
847                                         break;
848                                 case 'n': case 'N':
849                                         *mp++ = '\n';
850                                         break;
851                                 case 'f': case 'F':
852                                         *mp++ = '\f';
853                                         break;
854                                 case 'r': case 'R':
855                                         *mp++ = '\r';
856                                         break;
857                                 case 'e': case 'E':
858                                         *mp++ = ESC;
859                                         break;
860                                 case 'c': case 'C':
861                                         *mp++ = ':';
862                                         break;
863                                 default:
864                                         /*
865                                          * Catches '\', '^', and
866                                          *  everything else.
867                                          */
868                                         *mp++ = *(bp-1);
869                                         break;
870                         }
871                 } else
872                         *mp++ = *bp++;
873                 m_room--;
874
875                 /*
876                  * Enforce loop invariant: if no room left in current
877                  * buffer, try to get some more.
878                  */
879                 if (m_room == 0) {
880                         size_t size = mp - mem;
881
882                         if ((mem = reallocf(mem, size + SFRAG)) == NULL)
883                                 return (-2);
884                         m_room = SFRAG;
885                         mp = mem + size;
886                 }
887         }
888         *mp++ = '\0';   /* loop invariant let's us do this */
889         m_room--;
890         len = mp - mem - 1;
891
892         /*
893          * Give back any extra memory and return value and success.
894          */
895         if (m_room != 0)
896                 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
897                         return (-2);
898         *str = mem;
899         return (len);
900 }
901
902 /*
903  * Cgetustr retrieves the value of the string capability cap from the
904  * capability record pointed to by buf.  The difference between cgetustr()
905  * and cgetstr() is that cgetustr does not decode escapes but rather treats
906  * all characters literally.  A pointer to a  NUL terminated malloc'd
907  * copy of the string is returned in the char pointed to by str.  The
908  * length of the string not including the trailing NUL is returned on success,
909  * -1 if the requested string capability couldn't be found, -2 if a system
910  * error was encountered (storage allocation failure).
911  */
912 int
913 cgetustr(char *buf, const char *cap, char **str)
914 {
915         u_int m_room;
916         char *bp, *mp;
917         int len;
918         char *mem;
919
920         /*
921          * Find string capability cap
922          */
923         if ((bp = cgetcap(buf, cap, '=')) == NULL)
924                 return (-1);
925
926         /*
927          * Conversion / storage allocation loop ...  Allocate memory in
928          * chunks SFRAG in size.
929          */
930         if ((mem = malloc(SFRAG)) == NULL) {
931                 errno = ENOMEM;
932                 return (-2);    /* couldn't even allocate the first fragment */
933         }
934         m_room = SFRAG;
935         mp = mem;
936
937         while (*bp != ':' && *bp != '\0') {
938                 /*
939                  * Loop invariants:
940                  *      There is always room for one more character in mem.
941                  *      Mp always points just past last character in mem.
942                  *      Bp always points at next character in buf.
943                  */
944                 *mp++ = *bp++;
945                 m_room--;
946
947                 /*
948                  * Enforce loop invariant: if no room left in current
949                  * buffer, try to get some more.
950                  */
951                 if (m_room == 0) {
952                         size_t size = mp - mem;
953
954                         if ((mem = reallocf(mem, size + SFRAG)) == NULL)
955                                 return (-2);
956                         m_room = SFRAG;
957                         mp = mem + size;
958                 }
959         }
960         *mp++ = '\0';   /* loop invariant let's us do this */
961         m_room--;
962         len = mp - mem - 1;
963
964         /*
965          * Give back any extra memory and return value and success.
966          */
967         if (m_room != 0)
968                 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
969                         return (-2);
970         *str = mem;
971         return (len);
972 }
973
974 /*
975  * Cgetnum retrieves the value of the numeric capability cap from the
976  * capability record pointed to by buf.  The numeric value is returned in
977  * the long pointed to by num.  0 is returned on success, -1 if the requested
978  * numeric capability couldn't be found.
979  */
980 int
981 cgetnum(char *buf, const char *cap, long *num)
982 {
983         long n;
984         int base, digit;
985         char *bp;
986
987         /*
988          * Find numeric capability cap
989          */
990         bp = cgetcap(buf, cap, '#');
991         if (bp == NULL)
992                 return (-1);
993
994         /*
995          * Look at value and determine numeric base:
996          *      0x... or 0X...  hexadecimal,
997          * else 0...            octal,
998          * else                 decimal.
999          */
1000         if (*bp == '0') {
1001                 bp++;
1002                 if (*bp == 'x' || *bp == 'X') {
1003                         bp++;
1004                         base = 16;
1005                 } else
1006                         base = 8;
1007         } else
1008                 base = 10;
1009
1010         /*
1011          * Conversion loop ...
1012          */
1013         n = 0;
1014         for (;;) {
1015                 if ('0' <= *bp && *bp <= '9')
1016                         digit = *bp - '0';
1017                 else if ('a' <= *bp && *bp <= 'f')
1018                         digit = 10 + *bp - 'a';
1019                 else if ('A' <= *bp && *bp <= 'F')
1020                         digit = 10 + *bp - 'A';
1021                 else
1022                         break;
1023
1024                 if (digit >= base)
1025                         break;
1026
1027                 n = n * base + digit;
1028                 bp++;
1029         }
1030
1031         /*
1032          * Return value and success.
1033          */
1034         *num = n;
1035         return (0);
1036 }
1037
1038
1039 /*
1040  * Compare name field of record.
1041  */
1042 static int
1043 nfcmp(char *nf, char *rec)
1044 {
1045         char *cp, tmp;
1046         int ret;
1047
1048         for (cp = rec; *cp != ':'; cp++)
1049                 ;
1050
1051         tmp = *(cp + 1);
1052         *(cp + 1) = '\0';
1053         ret = strcmp(nf, rec);
1054         *(cp + 1) = tmp;
1055
1056         return (ret);
1057 }