xref: /dragonfly/lib/libc/gen/getcap.c (revision 49781055)
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  * $FreeBSD: src/lib/libc/gen/getcap.c,v 1.11.2.2 2001/01/15 06:48:09 gad Exp $
37  * $DragonFly: src/lib/libc/gen/getcap.c,v 1.7 2005/11/19 22:32:53 swildner Exp $
38  *
39  * @(#)getcap.c	8.3 (Berkeley) 3/25/94
40  */
41 
42 #include "namespace.h"
43 #include <sys/types.h>
44 
45 #include <ctype.h>
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 #include "un-namespace.h"
54 
55 #include <db.h>
56 
57 #define	BFRAG		1024
58 #define	BSIZE		1024
59 #define	ESC		('[' & 037)	/* ASCII ESC */
60 #define	MAX_RECURSION	32		/* maximum getent recursion */
61 #define	SFRAG		100		/* cgetstr mallocs in SFRAG chunks */
62 
63 #define RECOK	(char)0
64 #define TCERR	(char)1
65 #define	SHADOW	(char)2
66 
67 static size_t	 topreclen;	/* toprec length */
68 static char	*toprec;	/* Additional record specified by cgetset() */
69 static int	 gottoprec;	/* Flag indicating retrieval of toprecord */
70 
71 static int	cdbget (DB *, char **, char *);
72 static int 	getent (char **, u_int *, char **, int, char *, int, char *);
73 static int	nfcmp (char *, char *);
74 
75 /*
76  * Cgetset() allows the addition of a user specified buffer to be added
77  * to the database array, in effect "pushing" the buffer on top of the
78  * virtual database. 0 is returned on success, -1 on failure.
79  */
80 int
81 cgetset(char *ent)
82 {
83 	if (ent == NULL) {
84 		if (toprec)
85 			free(toprec);
86                 toprec = NULL;
87                 topreclen = 0;
88                 return (0);
89         }
90         topreclen = strlen(ent);
91         if ((toprec = malloc (topreclen + 1)) == NULL) {
92 		errno = ENOMEM;
93                 return (-1);
94 	}
95 	gottoprec = 0;
96         strcpy(toprec, ent);
97         return (0);
98 }
99 
100 /*
101  * Cgetcap searches the capability record buf for the capability cap with
102  * type `type'.  A pointer to the value of cap is returned on success, NULL
103  * if the requested capability couldn't be found.
104  *
105  * Specifying a type of ':' means that nothing should follow cap (:cap:).
106  * In this case a pointer to the terminating ':' or NUL will be returned if
107  * cap is found.
108  *
109  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
110  * return NULL.
111  */
112 char *
113 cgetcap(char *buf, char *cap, int type)
114 {
115 	char *bp, *cp;
116 
117 	bp = buf;
118 	for (;;) {
119 		/*
120 		 * Skip past the current capability field - it's either the
121 		 * name field if this is the first time through the loop, or
122 		 * the remainder of a field whose name failed to match cap.
123 		 */
124 		for (;;)
125 			if (*bp == '\0')
126 				return (NULL);
127 			else
128 				if (*bp++ == ':')
129 					break;
130 
131 		/*
132 		 * Try to match (cap, type) in buf.
133 		 */
134 		for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
135 			continue;
136 		if (*cp != '\0')
137 			continue;
138 		if (*bp == '@')
139 			return (NULL);
140 		if (type == ':') {
141 			if (*bp != '\0' && *bp != ':')
142 				continue;
143 			return(bp);
144 		}
145 		if (*bp != type)
146 			continue;
147 		bp++;
148 		return (*bp == '@' ? NULL : bp);
149 	}
150 	/* NOTREACHED */
151 }
152 
153 /*
154  * Cgetent extracts the capability record name from the NULL terminated file
155  * array db_array and returns a pointer to a malloc'd copy of it in buf.
156  * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
157  * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
158  * -1 if the requested record couldn't be found, -2 if a system error was
159  * encountered (couldn't open/read a file, etc.), and -3 if a potential
160  * reference loop is detected.
161  */
162 int
163 cgetent(char **buf, char **db_array, char *name)
164 {
165 	u_int dummy;
166 
167 	return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
168 }
169 
170 /*
171  * Getent implements the functions of cgetent.  If fd is non-negative,
172  * *db_array has already been opened and fd is the open file descriptor.  We
173  * do this to save time and avoid using up file descriptors for tc=
174  * recursions.
175  *
176  * Getent returns the same success/failure codes as cgetent.  On success, a
177  * pointer to a malloc'ed capability record with all tc= capabilities fully
178  * expanded and its length (not including trailing ASCII NUL) are left in
179  * *cap and *len.
180  *
181  * Basic algorithm:
182  *	+ Allocate memory incrementally as needed in chunks of size BFRAG
183  *	  for capability buffer.
184  *	+ Recurse for each tc=name and interpolate result.  Stop when all
185  *	  names interpolated, a name can't be found, or depth exceeds
186  *	  MAX_RECURSION.
187  */
188 static int
189 getent(char **cap, u_int *len, char **db_array, int fd, char *name, int depth,
190        char *nfield)
191 {
192 	DB *capdbp;
193 	char *r_end, *rp, **db_p;
194 	int myfd, eof, foundit, retval, clen;
195 	char *record, *cbuf;
196 	int tc_not_resolved;
197 	char pbuf[_POSIX_PATH_MAX];
198 
199 	rp = NULL;
200 	myfd = 0;
201 
202 	/*
203 	 * Return with ``loop detected'' error if we've recursed more than
204 	 * MAX_RECURSION times.
205 	 */
206 	if (depth > MAX_RECURSION)
207 		return (-3);
208 
209 	/*
210 	 * Check if we have a top record from cgetset().
211          */
212 	if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
213 		if ((record = malloc (topreclen + BFRAG)) == NULL) {
214 			errno = ENOMEM;
215 			return (-2);
216 		}
217 		strcpy(record, toprec);
218 		myfd = 0;
219 		db_p = db_array;
220 		rp = record + topreclen + 1;
221 		r_end = rp + BFRAG;
222 		goto tc_exp;
223 	}
224 	/*
225 	 * Allocate first chunk of memory.
226 	 */
227 	if ((record = malloc(BFRAG)) == NULL) {
228 		errno = ENOMEM;
229 		return (-2);
230 	}
231 	r_end = record + BFRAG;
232 	foundit = 0;
233 	/*
234 	 * Loop through database array until finding the record.
235 	 */
236 
237 	for (db_p = db_array; *db_p != NULL; db_p++) {
238 		eof = 0;
239 
240 		/*
241 		 * Open database if not already open.
242 		 */
243 
244 		if (fd >= 0) {
245 			lseek(fd, (off_t)0, SEEK_SET);
246 			myfd = 0;
247 		} else {
248 			snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
249 			if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
250 			     != NULL) {
251 				free(record);
252 				retval = cdbget(capdbp, &record, name);
253 				if (retval < 0) {
254 					/* no record available */
255 					capdbp->close(capdbp);
256 					return (retval);
257 				}
258 				/* save the data; close frees it */
259 				clen = strlen(record);
260 				cbuf = malloc(clen + 1);
261 				memcpy(cbuf, record, clen + 1);
262 				if (capdbp->close(capdbp) < 0) {
263 					free(cbuf);
264 					return (-2);
265 				}
266 				*len = clen;
267 				*cap = cbuf;
268 				return (retval);
269 			} else {
270 				fd = _open(*db_p, O_RDONLY, 0);
271 				if (fd < 0)
272 					continue;
273 				myfd = 1;
274 			}
275 		}
276 		/*
277 		 * Find the requested capability record ...
278 		 */
279 		{
280 		char buf[BUFSIZ];
281 		char *b_end, *bp;
282 		int c;
283 
284 		/*
285 		 * Loop invariants:
286 		 *	There is always room for one more character in record.
287 		 *	R_end always points just past end of record.
288 		 *	Rp always points just past last character in record.
289 		 *	B_end always points just past last character in buf.
290 		 *	Bp always points at next character in buf.
291 		 */
292 		b_end = buf;
293 		bp = buf;
294 		for (;;) {
295 
296 			/*
297 			 * Read in a line implementing (\, newline)
298 			 * line continuation.
299 			 */
300 			rp = record;
301 			for (;;) {
302 				if (bp >= b_end) {
303 					int n;
304 
305 					n = _read(fd, buf, sizeof(buf));
306 					if (n <= 0) {
307 						if (myfd)
308 							_close(fd);
309 						if (n < 0) {
310 							free(record);
311 							return (-2);
312 						} else {
313 							fd = -1;
314 							eof = 1;
315 							break;
316 						}
317 					}
318 					b_end = buf+n;
319 					bp = buf;
320 				}
321 
322 				c = *bp++;
323 				if (c == '\n') {
324 					if (rp > record && *(rp-1) == '\\') {
325 						rp--;
326 						continue;
327 					} else
328 						break;
329 				}
330 				*rp++ = c;
331 
332 				/*
333 				 * Enforce loop invariant: if no room
334 				 * left in record buffer, try to get
335 				 * some more.
336 				 */
337 				if (rp >= r_end) {
338 					u_int pos;
339 					size_t newsize;
340 
341 					pos = rp - record;
342 					newsize = r_end - record + BFRAG;
343 					record = reallocf(record, newsize);
344 					if (record == NULL) {
345 						errno = ENOMEM;
346 						if (myfd)
347 							_close(fd);
348 						return (-2);
349 					}
350 					r_end = record + newsize;
351 					rp = record + pos;
352 				}
353 			}
354 				/* loop invariant let's us do this */
355 			*rp++ = '\0';
356 
357 			/*
358 			 * If encountered eof check next file.
359 			 */
360 			if (eof)
361 				break;
362 
363 			/*
364 			 * Toss blank lines and comments.
365 			 */
366 			if (*record == '\0' || *record == '#')
367 				continue;
368 
369 			/*
370 			 * See if this is the record we want ...
371 			 */
372 			if (cgetmatch(record, name) == 0) {
373 				if (nfield == NULL || !nfcmp(nfield, record)) {
374 					foundit = 1;
375 					break;	/* found it! */
376 				}
377 			}
378 		}
379 	}
380 		if (foundit)
381 			break;
382 	}
383 
384 	if (!foundit) {
385 		free(record);
386 		return (-1);
387 	}
388 
389 	/*
390 	 * Got the capability record, but now we have to expand all tc=name
391 	 * references in it ...
392 	 */
393 tc_exp:	{
394 		char *newicap, *s;
395 		int newilen;
396 		u_int ilen;
397 		int diff, iret, tclen;
398 		char *icap, *scan, *tc, *tcstart, *tcend;
399 
400 		/*
401 		 * Loop invariants:
402 		 *	There is room for one more character in record.
403 		 *	R_end points just past end of record.
404 		 *	Rp points just past last character in record.
405 		 *	Scan points at remainder of record that needs to be
406 		 *	scanned for tc=name constructs.
407 		 */
408 		scan = record;
409 		tc_not_resolved = 0;
410 		for (;;) {
411 			if ((tc = cgetcap(scan, "tc", '=')) == NULL)
412 				break;
413 
414 			/*
415 			 * Find end of tc=name and stomp on the trailing `:'
416 			 * (if present) so we can use it to call ourselves.
417 			 */
418 			s = tc;
419 			for (;;)
420 				if (*s == '\0')
421 					break;
422 				else
423 					if (*s++ == ':') {
424 						*(s - 1) = '\0';
425 						break;
426 					}
427 			tcstart = tc - 3;
428 			tclen = s - tcstart;
429 			tcend = s;
430 
431 			iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
432 				      NULL);
433 			newicap = icap;		/* Put into a register. */
434 			newilen = ilen;
435 			if (iret != 0) {
436 				/* an error */
437 				if (iret < -1) {
438 					if (myfd)
439 						_close(fd);
440 					free(record);
441 					return (iret);
442 				}
443 				if (iret == 1)
444 					tc_not_resolved = 1;
445 				/* couldn't resolve tc */
446 				if (iret == -1) {
447 					*(s - 1) = ':';
448 					scan = s - 1;
449 					tc_not_resolved = 1;
450 					continue;
451 
452 				}
453 			}
454 			/* not interested in name field of tc'ed record */
455 			s = newicap;
456 			for (;;)
457 				if (*s == '\0')
458 					break;
459 				else
460 					if (*s++ == ':')
461 						break;
462 			newilen -= s - newicap;
463 			newicap = s;
464 
465 			/* make sure interpolated record is `:'-terminated */
466 			s += newilen;
467 			if (*(s-1) != ':') {
468 				*s = ':';	/* overwrite NUL with : */
469 				newilen++;
470 			}
471 
472 			/*
473 			 * Make sure there's enough room to insert the
474 			 * new record.
475 			 */
476 			diff = newilen - tclen;
477 			if (diff >= r_end - rp) {
478 				u_int pos, tcpos, tcposend;
479 				size_t newsize;
480 
481 				pos = rp - record;
482 				newsize = r_end - record + diff + BFRAG;
483 				tcpos = tcstart - record;
484 				tcposend = tcend - record;
485 				record = reallocf(record, newsize);
486 				if (record == NULL) {
487 					errno = ENOMEM;
488 					if (myfd)
489 						_close(fd);
490 					free(icap);
491 					return (-2);
492 				}
493 				r_end = record + newsize;
494 				rp = record + pos;
495 				tcstart = record + tcpos;
496 				tcend = record + tcposend;
497 			}
498 
499 			/*
500 			 * Insert tc'ed record into our record.
501 			 */
502 			s = tcstart + newilen;
503 			bcopy(tcend, s, rp - tcend);
504 			bcopy(newicap, tcstart, newilen);
505 			rp += diff;
506 			free(icap);
507 
508 			/*
509 			 * Start scan on `:' so next cgetcap works properly
510 			 * (cgetcap always skips first field).
511 			 */
512 			scan = s-1;
513 		}
514 
515 	}
516 	/*
517 	 * Close file (if we opened it), give back any extra memory, and
518 	 * return capability, length and success.
519 	 */
520 	if (myfd)
521 		_close(fd);
522 	*len = rp - record - 1;	/* don't count NUL */
523 	if (r_end > rp)
524 		if ((record =
525 		     reallocf(record, (size_t)(rp - record))) == NULL) {
526 			errno = ENOMEM;
527 			return (-2);
528 		}
529 
530 	*cap = record;
531 	if (tc_not_resolved)
532 		return (1);
533 	return (0);
534 }
535 
536 static int
537 cdbget(DB *capdbp, char **bp, char *name)
538 {
539 	DBT key, data;
540 
541 	key.data = name;
542 	key.size = strlen(name);
543 
544 	for (;;) {
545 		/* Get the reference. */
546 		switch(capdbp->get(capdbp, &key, &data, 0)) {
547 		case -1:
548 			return (-2);
549 		case 1:
550 			return (-1);
551 		}
552 
553 		/* If not an index to another record, leave. */
554 		if (((char *)data.data)[0] != SHADOW)
555 			break;
556 
557 		key.data = (char *)data.data + 1;
558 		key.size = data.size - 1;
559 	}
560 
561 	*bp = (char *)data.data + 1;
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  */
569 int
570 cgetmatch(char *buf, char *name)
571 {
572 	char *np, *bp;
573 
574 	/*
575 	 * Start search at beginning of record.
576 	 */
577 	bp = buf;
578 	for (;;) {
579 		/*
580 		 * Try to match a record name.
581 		 */
582 		np = name;
583 		for (;;)
584 			if (*np == '\0')
585 				if (*bp == '|' || *bp == ':' || *bp == '\0')
586 					return (0);
587 				else
588 					break;
589 			else
590 				if (*bp++ != *np++)
591 					break;
592 
593 		/*
594 		 * Match failed, skip to next name in record.
595 		 */
596 		bp--;	/* a '|' or ':' may have stopped the match */
597 		for (;;)
598 			if (*bp == '\0' || *bp == ':')
599 				return (-1);	/* match failed totally */
600 			else
601 				if (*bp++ == '|')
602 					break;	/* found next name */
603 	}
604 }
605 
606 
607 
608 
609 
610 int
611 cgetfirst(char **buf, char **db_array)
612 {
613 	cgetclose();
614 	return (cgetnext(buf, db_array));
615 }
616 
617 static FILE *pfp;
618 static int slash;
619 static char **dbp;
620 
621 int
622 cgetclose(void)
623 {
624 	if (pfp != NULL) {
625 		fclose(pfp);
626 		pfp = NULL;
627 	}
628 	dbp = NULL;
629 	gottoprec = 0;
630 	slash = 0;
631 	return(0);
632 }
633 
634 /*
635  * Cgetnext() gets either the first or next entry in the logical database
636  * specified by db_array.  It returns 0 upon completion of the database, 1
637  * upon returning an entry with more remaining, and -1 if an error occurs.
638  */
639 int
640 cgetnext(char **bp, char **db_array)
641 {
642 	size_t len;
643 	int done, hadreaderr, i, savederrno, status;
644 	char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
645 	u_int dummy;
646 
647 	savederrno = 0;
648 
649 	if (dbp == NULL)
650 		dbp = db_array;
651 
652 	if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
653 		cgetclose();
654 		return (-1);
655 	}
656 	for(;;) {
657 		if (toprec && !gottoprec) {
658 			gottoprec = 1;
659 			line = toprec;
660 		} else {
661 			line = fgetln(pfp, &len);
662 			if (line == NULL && pfp) {
663 				hadreaderr = ferror(pfp);
664 				if (hadreaderr)
665 					savederrno = errno;
666 				fclose(pfp);
667 				pfp = NULL;
668 				if (hadreaderr) {
669 					cgetclose();
670 					errno = savederrno;
671 					return (-1);
672 				} else {
673 					if (*++dbp == NULL) {
674 						cgetclose();
675 						return (0);
676 					} else if ((pfp =
677 					    fopen(*dbp, "r")) == NULL) {
678 						cgetclose();
679 						return (-1);
680 					} else
681 						continue;
682 				}
683 			} else
684 				line[len - 1] = '\0';
685 			if (len == 1) {
686 				slash = 0;
687 				continue;
688 			}
689 			if (isspace((unsigned char)*line) ||
690 			    *line == ':' || *line == '#' || slash) {
691 				if (line[len - 2] == '\\')
692 					slash = 1;
693 				else
694 					slash = 0;
695 				continue;
696 			}
697 			if (line[len - 2] == '\\')
698 				slash = 1;
699 			else
700 				slash = 0;
701 		}
702 
703 
704 		/*
705 		 * Line points to a name line.
706 		 */
707 		i = 0;
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 			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  */
779 int
780 cgetstr(char *buf, 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  */
906 int
907 cgetustr(char *buf, 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  */
974 int
975 cgetnum(char *buf, 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  */
1036 static int
1037 nfcmp(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