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