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