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