xref: /csrg-svn/lib/libc/gen/getcap.c (revision 56184)
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 		if ((record =
511 		     realloc(record, (size_t)(rp - record))) == NULL) {
512 			errno = ENOMEM;
513 			return (-2);
514 		}
515 
516 	*cap = record;
517 	if (tc_not_resolved)
518 		return (1);
519 	return (0);
520 }
521 
522 static int
523 cdbget(capdbp, bp, name)
524 	DB *capdbp;
525 	char **bp, *name;
526 {
527 	DBT key, data;
528 	char *buf;
529 	int st;
530 
531 	key.data = name;
532 	key.size = strlen(name) + 1;
533 
534 	if ((st = capdbp->get(capdbp, &key, &data, 0)) < 0)
535 		return(-2);
536 	if (st == 1)
537 		return(-1);
538 
539 	if (((char *)(data.data))[0] == RECORD) {
540 		*bp = &((char *)(data.data))[1];
541 		return(0);
542 	}
543 	if ((buf = malloc(data.size - 1)) == NULL)
544 		return(-2);
545 
546 	strcpy(buf, &((char *)(data.data))[1]);
547 
548 	key.data = buf;
549 	key.size = data.size - 1;
550 
551 	if (capdbp->get(capdbp, &key, &data, 0) < 0) {
552 		free(buf);
553 		return(-2);
554 	}
555 	free(buf);
556 	*bp = &((char *)(data.data))[1];
557 	return(0);
558 }
559 
560 
561 /*
562  * Cgetmatch will return 0 if name is one of the names of the capability
563  * record buf, -1 if not.
564  */
565 int
566 cgetmatch(buf, name)
567 	char *buf, *name;
568 {
569 	register char *np, *bp;
570 
571 	/*
572 	 * Start search at beginning of record.
573 	 */
574 	bp = buf;
575 	for (;;) {
576 		/*
577 		 * Try to match a record name.
578 		 */
579 		np = name;
580 		for (;;)
581 			if (*np == '\0')
582 				if (*bp == '|' || *bp == ':' || *bp == '\0')
583 					return (0);
584 				else
585 					break;
586 			else
587 				if (*bp++ != *np++)
588 					break;
589 
590 		/*
591 		 * Match failed, skip to next name in record.
592 		 */
593 		bp--;	/* a '|' or ':' may have stopped the match */
594 		for (;;)
595 			if (*bp == '\0' || *bp == ':')
596 				return (-1);	/* match failed totally */
597 			else
598 				if (*bp++ == '|')
599 					break;	/* found next name */
600 	}
601 }
602 
603 
604 
605 
606 
607 int
608 cgetfirst(buf, db_array)
609 	char **buf, **db_array;
610 {
611 	(void)cgetclose();
612 	return (cgetnext(buf, db_array));
613 }
614 
615 static FILE *pfp;
616 static int slash;
617 static char **dbp;
618 
619 int
620 cgetclose()
621 {
622 	if (pfp != NULL) {
623 		(void)fclose(pfp);
624 		pfp = NULL;
625 	}
626 	dbp = NULL;
627 	gottoprec = 0;
628 	slash = 0;
629 	return(0);
630 }
631 
632 /*
633  * Cgetnext() gets either the first or next entry in the logical database
634  * specified by db_array.  It returns 0 upon completion of the database, 1
635  * upon returning an entry with more remaining, and -1 if an error occurs.
636  */
637 int
638 cgetnext(bp, db_array)
639         register char **bp;
640 	char **db_array;
641 {
642 	size_t len;
643 	int status;
644 	char *cp, *line, *rp, buf[BSIZE];
645 	u_int dummy;
646 
647 	if (dbp == NULL)
648 		dbp = db_array;
649 
650 	if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
651 		(void)cgetclose();
652 		return (-1);
653 	}
654 	for(;;) {
655 		if (toprec && !gottoprec) {
656 			gottoprec = 1;
657 			line = toprec;
658 		} else {
659 			line = fgetline(pfp, &len);
660 			if (line == NULL && pfp) {
661 				(void)fclose(pfp);
662 				if (ferror(pfp)) {
663 					(void)cgetclose();
664 					return (-1);
665 				} else {
666 					dbp++;
667 					if (*dbp == NULL) {
668 						(void)cgetclose();
669 						return (0);
670 					} else if ((pfp = fopen(*dbp, "r")) ==
671 					    NULL) {
672 						(void)cgetclose();
673 						return (-1);
674 					} else
675 						continue;
676 				}
677 			}
678 			if (isspace(*line) || *line == ':' || *line == '#'
679 			    || len == 0 || slash) {
680 				if (len > 0 && line[len - 1] == '\\')
681 					slash = 1;
682 				else
683 					slash = 0;
684 				continue;
685 			}
686 			if (len > 0 && line[len - 1] == '\\')
687 				slash = 1;
688 			else
689 				slash = 0;
690 		}
691 		/* line points to a name line */
692 
693 		rp = buf;
694 		for(cp = line; *cp != NULL; cp++)
695 			if (*cp == '|' || *cp == ':')
696 				break;
697 			else
698 				*rp++ = *cp;
699 
700 		*rp = '\0';
701 		status = getent(bp, &dummy, db_array, -1, buf, 0);
702 		if (status == -2 || status == -3)
703 			(void)cgetclose();
704 
705 		return (status + 1);
706 	}
707 	/* NOTREACHED */
708 }
709 
710 /*
711  * Cgetstr retrieves the value of the string capability cap from the
712  * capability record pointed to by buf.  A pointer to a decoded, NUL
713  * terminated, malloc'd copy of the string is returned in the char *
714  * pointed to by str.  The length of the string not including the trailing
715  * NUL is returned on success, -1 if the requested string capability
716  * couldn't be found, -2 if a system error was encountered (storage
717  * allocation failure).
718  */
719 int
720 cgetstr(buf, cap, str)
721 	char *buf, *cap;
722 	char **str;
723 {
724 	register u_int m_room;
725 	register char *bp, *mp;
726 	int len;
727 	char *mem;
728 
729 	/*
730 	 * Find string capability cap
731 	 */
732 	bp = cgetcap(buf, cap, '=');
733 	if (bp == NULL)
734 		return (-1);
735 
736 	/*
737 	 * Conversion / storage allocation loop ...  Allocate memory in
738 	 * chunks SFRAG in size.
739 	 */
740 	if ((mem = malloc(SFRAG)) == NULL) {
741 		errno = ENOMEM;
742 		return (-2);	/* couldn't even allocate the first fragment */
743 	}
744 	m_room = SFRAG;
745 	mp = mem;
746 
747 	while (*bp != ':' && *bp != '\0') {
748 		/*
749 		 * Loop invariants:
750 		 *	There is always room for one more character in mem.
751 		 *	Mp always points just past last character in mem.
752 		 *	Bp always points at next character in buf.
753 		 */
754 		if (*bp == '^') {
755 			bp++;
756 			if (*bp == ':' || *bp == '\0')
757 				break;	/* drop unfinished escape */
758 			*mp++ = *bp++ & 037;
759 		} else if (*bp == '\\') {
760 			bp++;
761 			if (*bp == ':' || *bp == '\0')
762 				break;	/* drop unfinished escape */
763 			if ('0' <= *bp && *bp <= '7') {
764 				register int n, i;
765 
766 				n = 0;
767 				i = 3;	/* maximum of three octal digits */
768 				do {
769 					n = n * 8 + (*bp++ - '0');
770 				} while (--i && '0' <= *bp && *bp <= '7');
771 				*mp++ = n;
772 			}
773 			else switch (*bp++) {
774 				case 'b': case 'B':
775 					*mp++ = '\b';
776 					break;
777 				case 't': case 'T':
778 					*mp++ = '\t';
779 					break;
780 				case 'n': case 'N':
781 					*mp++ = '\n';
782 					break;
783 				case 'f': case 'F':
784 					*mp++ = '\f';
785 					break;
786 				case 'r': case 'R':
787 					*mp++ = '\r';
788 					break;
789 				case 'e': case 'E':
790 					*mp++ = ESC;
791 					break;
792 				case 'c': case 'C':
793 					*mp++ = ':';
794 					break;
795 				default:
796 					/*
797 					 * Catches '\', '^', and
798 					 *  everything else.
799 					 */
800 					*mp++ = *(bp-1);
801 					break;
802 			}
803 		} else
804 			*mp++ = *bp++;
805 		m_room--;
806 
807 		/*
808 		 * Enforce loop invariant: if no room left in current
809 		 * buffer, try to get some more.
810 		 */
811 		if (m_room == 0) {
812 			size_t size = mp - mem;
813 
814 			if ((mem = realloc(mem, size + SFRAG)) == NULL)
815 				return (-2);
816 			m_room = SFRAG;
817 			mp = mem + size;
818 		}
819 	}
820 	*mp++ = '\0';	/* loop invariant let's us do this */
821 	m_room--;
822 	len = mp - mem - 1;
823 
824 	/*
825 	 * Give back any extra memory and return value and success.
826 	 */
827 	if (m_room != 0)
828 		if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
829 			return (-2);
830 	*str = mem;
831 	return (len);
832 }
833 
834 /*
835  * Cgetustr retrieves the value of the string capability cap from the
836  * capability record pointed to by buf.  The difference between cgetustr()
837  * and cgetstr() is that cgetustr does not decode escapes but rather treats
838  * all characters literally.  A pointer to a  NUL terminated malloc'd
839  * copy of the string is returned in the char pointed to by str.  The
840  * length of the string not including the trailing NUL is returned on success,
841  * -1 if the requested string capability couldn't be found, -2 if a system
842  * error was encountered (storage allocation failure).
843  */
844 int
845 cgetustr(buf, cap, str)
846 	char *buf, *cap, **str;
847 {
848 	register u_int m_room;
849 	register char *bp, *mp;
850 	int len;
851 	char *mem;
852 
853 	/*
854 	 * Find string capability cap
855 	 */
856 	if ((bp = cgetcap(buf, cap, '=')) == NULL)
857 		return (-1);
858 
859 	/*
860 	 * Conversion / storage allocation loop ...  Allocate memory in
861 	 * chunks SFRAG in size.
862 	 */
863 	if ((mem = malloc(SFRAG)) == NULL) {
864 		errno = ENOMEM;
865 		return (-2);	/* couldn't even allocate the first fragment */
866 	}
867 	m_room = SFRAG;
868 	mp = mem;
869 
870 	while (*bp != ':' && *bp != '\0') {
871 		/*
872 		 * Loop invariants:
873 		 *	There is always room for one more character in mem.
874 		 *	Mp always points just past last character in mem.
875 		 *	Bp always points at next character in buf.
876 		 */
877 		*mp++ = *bp++;
878 		m_room--;
879 
880 		/*
881 		 * Enforce loop invariant: if no room left in current
882 		 * buffer, try to get some more.
883 		 */
884 		if (m_room == 0) {
885 			size_t size = mp - mem;
886 
887 			if ((mem = realloc(mem, size + SFRAG)) == NULL)
888 				return (-2);
889 			m_room = SFRAG;
890 			mp = mem + size;
891 		}
892 	}
893 	*mp++ = '\0';	/* loop invariant let's us do this */
894 	m_room--;
895 	len = mp - mem - 1;
896 
897 	/*
898 	 * Give back any extra memory and return value and success.
899 	 */
900 	if (m_room != 0)
901 		if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
902 			return (-2);
903 	*str = mem;
904 	return (len);
905 }
906 
907 /*
908  * Cgetnum retrieves the value of the numeric capability cap from the
909  * capability record pointed to by buf.  The numeric value is returned in
910  * the long pointed to by num.  0 is returned on success, -1 if the requested
911  * numeric capability couldn't be found.
912  */
913 int
914 cgetnum(buf, cap, num)
915 	char *buf, *cap;
916 	long *num;
917 {
918 	register long n;
919 	register int base, digit;
920 	register char *bp;
921 
922 	/*
923 	 * Find numeric capability cap
924 	 */
925 	bp = cgetcap(buf, cap, '#');
926 	if (bp == NULL)
927 		return (-1);
928 
929 	/*
930 	 * Look at value and determine numeric base:
931 	 *	0x... or 0X...	hexadecimal,
932 	 * else	0...		octal,
933 	 * else			decimal.
934 	 */
935 	if (*bp == '0') {
936 		bp++;
937 		if (*bp == 'x' || *bp == 'X') {
938 			bp++;
939 			base = 16;
940 		} else
941 			base = 8;
942 	} else
943 		base = 10;
944 
945 	/*
946 	 * Conversion loop ...
947 	 */
948 	n = 0;
949 	for (;;) {
950 		if ('0' <= *bp && *bp <= '9')
951 			digit = *bp - '0';
952 		else
953 			if (   ('a' <= *bp && *bp <= 'f')
954 			    || ('A' <= *bp && *bp <= 'F'))
955 				digit = 10 + *bp - 'a';
956 			else
957 				break;
958 
959 		if (digit >= base)
960 			break;
961 
962 		n = n * base + digit;
963 		bp++;
964 	}
965 
966 	/*
967 	 * Return value and success.
968 	 */
969 	*num = n;
970 	return (0);
971 }
972