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