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