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