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