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