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