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