GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/gen/getcap.c Lines: 0 383 0.0 %
Date: 2017-11-07 Branches: 0 319 0.0 %

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