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