1cd4b894aSbostic /*-
2bac379f5Sbostic * Copyright (c) 1992, 1993
3bac379f5Sbostic * The Regents of the University of California. All rights reserved.
4cd4b894aSbostic *
5cd4b894aSbostic * This code is derived from software contributed to Berkeley by
6cd4b894aSbostic * Casey Leedom of Lawrence Livermore National Laboratory.
7cd4b894aSbostic *
8b9edf1bcSralph * %sccs.include.redist.c%
9cd4b894aSbostic */
10cd4b894aSbostic
11cd4b894aSbostic #if defined(LIBC_SCCS) && !defined(lint)
12*eaff8629Sbostic static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 03/25/94";
13cd4b894aSbostic #endif /* LIBC_SCCS and not lint */
14cd4b894aSbostic
15cd4b894aSbostic #include <sys/types.h>
16cd4b894aSbostic
17cd4b894aSbostic #include <ctype.h>
18f032f707Selan #include <db.h>
19cd4b894aSbostic #include <errno.h>
20cd4b894aSbostic #include <fcntl.h>
21f032f707Selan #include <limits.h>
22cd4b894aSbostic #include <stdio.h>
23cd4b894aSbostic #include <stdlib.h>
24cd4b894aSbostic #include <string.h>
25cd4b894aSbostic #include <unistd.h>
26cd4b894aSbostic
27cd4b894aSbostic #define BFRAG 1024
285e4d7f0dSelan #define BSIZE 1024
29cd4b894aSbostic #define ESC ('[' & 037) /* ASCII ESC */
30cd4b894aSbostic #define MAX_RECURSION 32 /* maximum getent recursion */
31cd4b894aSbostic #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
32cd4b894aSbostic
33c2d10093Selan #define RECOK (char)0
34c2d10093Selan #define TCERR (char)1
35f3be1ef2Sbostic #define SHADOW (char)2
36f032f707Selan
37cd4b894aSbostic static size_t topreclen; /* toprec length */
38cd4b894aSbostic static char *toprec; /* Additional record specified by cgetset() */
39f6291f8aSelan static int gottoprec; /* Flag indicating retrieval of toprecord */
40cd4b894aSbostic
41f032f707Selan static int cdbget __P((DB *, char **, char *));
425e4d7f0dSelan static int getent __P((char **, u_int *, char **, int, char *, int, char *));
435e4d7f0dSelan static int nfcmp __P((char *, char *));
44cd4b894aSbostic
45cd4b894aSbostic /*
46cd4b894aSbostic * Cgetset() allows the addition of a user specified buffer to be added
47cd4b894aSbostic * to the database array, in effect "pushing" the buffer on top of the
48cd4b894aSbostic * virtual database. 0 is returned on success, -1 on failure.
49cd4b894aSbostic */
50cd4b894aSbostic int
cgetset(ent)51cd4b894aSbostic cgetset(ent)
52cd4b894aSbostic char *ent;
53cd4b894aSbostic {
54cd4b894aSbostic if (ent == NULL) {
55cd4b894aSbostic if (toprec)
56cd4b894aSbostic free(toprec);
57cd4b894aSbostic toprec = NULL;
58cd4b894aSbostic topreclen = 0;
59cd4b894aSbostic return (0);
60cd4b894aSbostic }
61cd4b894aSbostic topreclen = strlen(ent);
62cd4b894aSbostic if ((toprec = malloc (topreclen + 1)) == NULL) {
63cd4b894aSbostic errno = ENOMEM;
64cd4b894aSbostic return (-1);
65cd4b894aSbostic }
66366565adSelan gottoprec = 0;
67cd4b894aSbostic (void)strcpy(toprec, ent);
68cd4b894aSbostic return (0);
69cd4b894aSbostic }
70cd4b894aSbostic
71cd4b894aSbostic /*
72cd4b894aSbostic * Cgetcap searches the capability record buf for the capability cap with
73cd4b894aSbostic * type `type'. A pointer to the value of cap is returned on success, NULL
74cd4b894aSbostic * if the requested capability couldn't be found.
75cd4b894aSbostic *
76cd4b894aSbostic * Specifying a type of ':' means that nothing should follow cap (:cap:).
77cd4b894aSbostic * In this case a pointer to the terminating ':' or NUL will be returned if
78cd4b894aSbostic * cap is found.
79cd4b894aSbostic *
80cd4b894aSbostic * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
81cd4b894aSbostic * return NULL.
82cd4b894aSbostic */
83cd4b894aSbostic char *
cgetcap(buf,cap,type)84cd4b894aSbostic cgetcap(buf, cap, type)
85cd4b894aSbostic char *buf, *cap;
86cd4b894aSbostic int type;
87cd4b894aSbostic {
88cd4b894aSbostic register char *bp, *cp;
89cd4b894aSbostic
90cd4b894aSbostic bp = buf;
91cd4b894aSbostic for (;;) {
92cd4b894aSbostic /*
93cd4b894aSbostic * Skip past the current capability field - it's either the
94cd4b894aSbostic * name field if this is the first time through the loop, or
95cd4b894aSbostic * the remainder of a field whose name failed to match cap.
96cd4b894aSbostic */
97cd4b894aSbostic for (;;)
98cd4b894aSbostic if (*bp == '\0')
99cd4b894aSbostic return (NULL);
100cd4b894aSbostic else
101cd4b894aSbostic if (*bp++ == ':')
102cd4b894aSbostic break;
103cd4b894aSbostic
104cd4b894aSbostic /*
105cd4b894aSbostic * Try to match (cap, type) in buf.
106cd4b894aSbostic */
107cd4b894aSbostic for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
108cd4b894aSbostic continue;
109cd4b894aSbostic if (*cp != '\0')
110cd4b894aSbostic continue;
111cd4b894aSbostic if (*bp == '@')
112cd4b894aSbostic return (NULL);
113cd4b894aSbostic if (type == ':') {
114cd4b894aSbostic if (*bp != '\0' && *bp != ':')
115cd4b894aSbostic continue;
116cd4b894aSbostic return(bp);
117cd4b894aSbostic }
118cd4b894aSbostic if (*bp != type)
119cd4b894aSbostic continue;
120cd4b894aSbostic bp++;
121cd4b894aSbostic return (*bp == '@' ? NULL : bp);
122cd4b894aSbostic }
123cd4b894aSbostic /* NOTREACHED */
124cd4b894aSbostic }
125cd4b894aSbostic
126cd4b894aSbostic /*
127cd4b894aSbostic * Cgetent extracts the capability record name from the NULL terminated file
128cd4b894aSbostic * array db_array and returns a pointer to a malloc'd copy of it in buf.
129cd4b894aSbostic * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
130cd4b894aSbostic * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success,
131cd4b894aSbostic * -1 if the requested record couldn't be found, -2 if a system error was
132cd4b894aSbostic * encountered (couldn't open/read a file, etc.), and -3 if a potential
133cd4b894aSbostic * reference loop is detected.
134cd4b894aSbostic */
135cd4b894aSbostic int
cgetent(buf,db_array,name)136cd4b894aSbostic cgetent(buf, db_array, name)
137cd4b894aSbostic char **buf, **db_array, *name;
138cd4b894aSbostic {
139cd4b894aSbostic u_int dummy;
140cd4b894aSbostic
1415e4d7f0dSelan return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
142cd4b894aSbostic }
143cd4b894aSbostic
144cd4b894aSbostic /*
145cd4b894aSbostic * Getent implements the functions of cgetent. If fd is non-negative,
146cd4b894aSbostic * *db_array has already been opened and fd is the open file descriptor. We
147cd4b894aSbostic * do this to save time and avoid using up file descriptors for tc=
148cd4b894aSbostic * recursions.
149cd4b894aSbostic *
150cd4b894aSbostic * Getent returns the same success/failure codes as cgetent. On success, a
151cd4b894aSbostic * pointer to a malloc'ed capability record with all tc= capabilities fully
152cd4b894aSbostic * expanded and its length (not including trailing ASCII NUL) are left in
153cd4b894aSbostic * *cap and *len.
154cd4b894aSbostic *
155cd4b894aSbostic * Basic algorithm:
156cd4b894aSbostic * + Allocate memory incrementally as needed in chunks of size BFRAG
157cd4b894aSbostic * for capability buffer.
158cd4b894aSbostic * + Recurse for each tc=name and interpolate result. Stop when all
159cd4b894aSbostic * names interpolated, a name can't be found, or depth exceeds
160cd4b894aSbostic * MAX_RECURSION.
161cd4b894aSbostic */
162cd4b894aSbostic static int
getent(cap,len,db_array,fd,name,depth,nfield)1635e4d7f0dSelan getent(cap, len, db_array, fd, name, depth, nfield)
1645e4d7f0dSelan char **cap, **db_array, *name, *nfield;
165cd4b894aSbostic u_int *len;
166cd4b894aSbostic int fd, depth;
167cd4b894aSbostic {
168f032f707Selan DB *capdbp;
169f032f707Selan DBT key, data;
170cd4b894aSbostic register char *r_end, *rp, **db_p;
171*eaff8629Sbostic int myfd, eof, foundit, retval, clen;
172*eaff8629Sbostic char *record, *cbuf;
173f032f707Selan int tc_not_resolved;
174f032f707Selan char pbuf[_POSIX_PATH_MAX];
175cd4b894aSbostic
176cd4b894aSbostic /*
177cd4b894aSbostic * Return with ``loop detected'' error if we've recursed more than
178cd4b894aSbostic * MAX_RECURSION times.
179cd4b894aSbostic */
180cd4b894aSbostic if (depth > MAX_RECURSION)
181cd4b894aSbostic return (-3);
182cd4b894aSbostic
183cd4b894aSbostic /*
184cd4b894aSbostic * Check if we have a top record from cgetset().
185cd4b894aSbostic */
186366565adSelan if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
187cd4b894aSbostic if ((record = malloc (topreclen + BFRAG)) == NULL) {
188cd4b894aSbostic errno = ENOMEM;
189cd4b894aSbostic return (-2);
190cd4b894aSbostic }
191cd4b894aSbostic (void)strcpy(record, toprec);
192cd4b894aSbostic myfd = 0;
193cd4b894aSbostic db_p = db_array;
194cd4b894aSbostic rp = record + topreclen + 1;
195cd4b894aSbostic r_end = rp + BFRAG;
196cd4b894aSbostic goto tc_exp;
197cd4b894aSbostic }
198cd4b894aSbostic /*
199cd4b894aSbostic * Allocate first chunk of memory.
200cd4b894aSbostic */
201cd4b894aSbostic if ((record = malloc(BFRAG)) == NULL) {
202cd4b894aSbostic errno = ENOMEM;
203cd4b894aSbostic return (-2);
204cd4b894aSbostic }
205cd4b894aSbostic r_end = record + BFRAG;
206cd4b894aSbostic foundit = 0;
207cd4b894aSbostic /*
208cd4b894aSbostic * Loop through database array until finding the record.
209cd4b894aSbostic */
210cd4b894aSbostic
211cd4b894aSbostic for (db_p = db_array; *db_p != NULL; db_p++) {
212cd4b894aSbostic eof = 0;
213cd4b894aSbostic
214cd4b894aSbostic /*
215cd4b894aSbostic * Open database if not already open.
216cd4b894aSbostic */
217f032f707Selan
218cd4b894aSbostic if (fd >= 0) {
21919cbfad9Sbostic (void)lseek(fd, (off_t)0, L_SET);
220cd4b894aSbostic myfd = 0;
221cd4b894aSbostic } else {
22219cbfad9Sbostic (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
223f032f707Selan if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
224f032f707Selan != NULL) {
225f032f707Selan free(record);
226f032f707Selan retval = cdbget(capdbp, &record, name);
227*eaff8629Sbostic if (retval < 0) {
228*eaff8629Sbostic /* no record available */
229*eaff8629Sbostic (void)capdbp->close(capdbp);
230*eaff8629Sbostic return (retval);
231*eaff8629Sbostic }
232*eaff8629Sbostic /* save the data; close frees it */
233*eaff8629Sbostic clen = strlen(record);
234*eaff8629Sbostic cbuf = malloc(clen + 1);
235*eaff8629Sbostic memcpy(cbuf, record, clen + 1);
236*eaff8629Sbostic if (capdbp->close(capdbp) < 0) {
237*eaff8629Sbostic free(cbuf);
238f032f707Selan return (-2);
239*eaff8629Sbostic }
240*eaff8629Sbostic *len = clen;
241*eaff8629Sbostic *cap = cbuf;
242f032f707Selan return (retval);
243f032f707Selan } else {
244cd4b894aSbostic fd = open(*db_p, O_RDONLY, 0);
245cd4b894aSbostic if (fd < 0) {
246b45e9a50Selan /* No error on unfound file. */
247b45e9a50Selan if (errno == ENOENT)
248b45e9a50Selan continue;
249cd4b894aSbostic free(record);
250cd4b894aSbostic return (-2);
251cd4b894aSbostic }
252cd4b894aSbostic myfd = 1;
253cd4b894aSbostic }
254f032f707Selan }
255cd4b894aSbostic /*
256cd4b894aSbostic * Find the requested capability record ...
257cd4b894aSbostic */
258cd4b894aSbostic {
259cd4b894aSbostic char buf[BUFSIZ];
260cd4b894aSbostic register char *b_end, *bp;
261cd4b894aSbostic register int c;
262cd4b894aSbostic
263cd4b894aSbostic /*
264cd4b894aSbostic * Loop invariants:
265cd4b894aSbostic * There is always room for one more character in record.
266cd4b894aSbostic * R_end always points just past end of record.
267cd4b894aSbostic * Rp always points just past last character in record.
268cd4b894aSbostic * B_end always points just past last character in buf.
269cd4b894aSbostic * Bp always points at next character in buf.
270cd4b894aSbostic */
271cd4b894aSbostic b_end = buf;
272cd4b894aSbostic bp = buf;
273cd4b894aSbostic for (;;) {
274cd4b894aSbostic
275cd4b894aSbostic /*
276cd4b894aSbostic * Read in a line implementing (\, newline)
277cd4b894aSbostic * line continuation.
278cd4b894aSbostic */
279cd4b894aSbostic rp = record;
280cd4b894aSbostic for (;;) {
281cd4b894aSbostic if (bp >= b_end) {
282cd4b894aSbostic int n;
283cd4b894aSbostic
284cd4b894aSbostic n = read(fd, buf, sizeof(buf));
285cd4b894aSbostic if (n <= 0) {
286cd4b894aSbostic if (myfd)
287cd4b894aSbostic (void)close(fd);
288cd4b894aSbostic if (n < 0) {
289cd4b894aSbostic free(record);
290cd4b894aSbostic return (-2);
291cd4b894aSbostic } else {
292cd4b894aSbostic fd = -1;
293cd4b894aSbostic eof = 1;
294cd4b894aSbostic break;
295cd4b894aSbostic }
296cd4b894aSbostic }
297cd4b894aSbostic b_end = buf+n;
298cd4b894aSbostic bp = buf;
299cd4b894aSbostic }
300cd4b894aSbostic
301cd4b894aSbostic c = *bp++;
302cd4b894aSbostic if (c == '\n') {
303cd4b894aSbostic if (rp > record && *(rp-1) == '\\') {
304cd4b894aSbostic rp--;
305cd4b894aSbostic continue;
306cd4b894aSbostic } else
307cd4b894aSbostic break;
308cd4b894aSbostic }
309cd4b894aSbostic *rp++ = c;
310cd4b894aSbostic
311cd4b894aSbostic /*
312cd4b894aSbostic * Enforce loop invariant: if no room
313cd4b894aSbostic * left in record buffer, try to get
314cd4b894aSbostic * some more.
315cd4b894aSbostic */
316cd4b894aSbostic if (rp >= r_end) {
317f6291f8aSelan u_int pos;
318f6291f8aSelan size_t newsize;
319cd4b894aSbostic
320cd4b894aSbostic pos = rp - record;
321cd4b894aSbostic newsize = r_end - record + BFRAG;
322cd4b894aSbostic record = realloc(record, newsize);
323cd4b894aSbostic if (record == NULL) {
324cd4b894aSbostic errno = ENOMEM;
325cd4b894aSbostic if (myfd)
326cd4b894aSbostic (void)close(fd);
327cd4b894aSbostic return (-2);
328cd4b894aSbostic }
329cd4b894aSbostic r_end = record + newsize;
330cd4b894aSbostic rp = record + pos;
331cd4b894aSbostic }
332cd4b894aSbostic }
333cd4b894aSbostic /* loop invariant let's us do this */
334cd4b894aSbostic *rp++ = '\0';
335cd4b894aSbostic
336cd4b894aSbostic /*
337cd4b894aSbostic * If encountered eof check next file.
338cd4b894aSbostic */
339cd4b894aSbostic if (eof)
340cd4b894aSbostic break;
341cd4b894aSbostic
342cd4b894aSbostic /*
343cd4b894aSbostic * Toss blank lines and comments.
344cd4b894aSbostic */
345cd4b894aSbostic if (*record == '\0' || *record == '#')
346cd4b894aSbostic continue;
347cd4b894aSbostic
348cd4b894aSbostic /*
349cd4b894aSbostic * See if this is the record we want ...
350cd4b894aSbostic */
351cd4b894aSbostic if (cgetmatch(record, name) == 0) {
3525e4d7f0dSelan if (nfield == NULL || !nfcmp(nfield, record)) {
353cd4b894aSbostic foundit = 1;
354cd4b894aSbostic break; /* found it! */
355cd4b894aSbostic }
356cd4b894aSbostic }
357cd4b894aSbostic }
3585e4d7f0dSelan }
359cd4b894aSbostic if (foundit)
360cd4b894aSbostic break;
361cd4b894aSbostic }
362cd4b894aSbostic
363cd4b894aSbostic if (!foundit)
364cd4b894aSbostic return (-1);
365cd4b894aSbostic
366cd4b894aSbostic /*
367cd4b894aSbostic * Got the capability record, but now we have to expand all tc=name
368cd4b894aSbostic * references in it ...
369cd4b894aSbostic */
370cd4b894aSbostic tc_exp: {
371cd4b894aSbostic register char *newicap, *s;
372cd4b894aSbostic register int newilen;
373cd4b894aSbostic u_int ilen;
374cd4b894aSbostic int diff, iret, tclen;
375cd4b894aSbostic char *icap, *scan, *tc, *tcstart, *tcend;
376cd4b894aSbostic
377cd4b894aSbostic /*
378cd4b894aSbostic * Loop invariants:
379cd4b894aSbostic * There is room for one more character in record.
380cd4b894aSbostic * R_end points just past end of record.
381cd4b894aSbostic * Rp points just past last character in record.
382cd4b894aSbostic * Scan points at remainder of record that needs to be
383cd4b894aSbostic * scanned for tc=name constructs.
384cd4b894aSbostic */
385cd4b894aSbostic scan = record;
386f032f707Selan tc_not_resolved = 0;
387cd4b894aSbostic for (;;) {
388cd4b894aSbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL)
389cd4b894aSbostic break;
390cd4b894aSbostic
391cd4b894aSbostic /*
392cd4b894aSbostic * Find end of tc=name and stomp on the trailing `:'
393cd4b894aSbostic * (if present) so we can use it to call ourselves.
394cd4b894aSbostic */
395cd4b894aSbostic s = tc;
396cd4b894aSbostic for (;;)
397cd4b894aSbostic if (*s == '\0')
398cd4b894aSbostic break;
399cd4b894aSbostic else
400cd4b894aSbostic if (*s++ == ':') {
401cd4b894aSbostic *(s - 1) = '\0';
402cd4b894aSbostic break;
403cd4b894aSbostic }
404cd4b894aSbostic tcstart = tc - 3;
405cd4b894aSbostic tclen = s - tcstart;
406cd4b894aSbostic tcend = s;
407cd4b894aSbostic
4085e4d7f0dSelan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
4095e4d7f0dSelan NULL);
410cd4b894aSbostic newicap = icap; /* Put into a register. */
411cd4b894aSbostic newilen = ilen;
412cd4b894aSbostic if (iret != 0) {
413c2d10093Selan /* an error */
414c2d10093Selan if (iret < -1) {
415c2d10093Selan if (myfd)
416c2d10093Selan (void)close(fd);
417c2d10093Selan free(record);
418c2d10093Selan return (iret);
419c2d10093Selan }
420f032f707Selan if (iret == 1)
421f032f707Selan tc_not_resolved = 1;
422c2d10093Selan /* couldn't resolve tc */
423f032f707Selan if (iret == -1) {
424f032f707Selan *(s - 1) = ':';
425f032f707Selan scan = s - 1;
426f032f707Selan tc_not_resolved = 1;
427f032f707Selan continue;
428f032f707Selan
429f032f707Selan }
430cd4b894aSbostic }
431cd4b894aSbostic /* not interested in name field of tc'ed record */
432cd4b894aSbostic s = newicap;
433cd4b894aSbostic for (;;)
434cd4b894aSbostic if (*s == '\0')
435cd4b894aSbostic break;
436cd4b894aSbostic else
437cd4b894aSbostic if (*s++ == ':')
438cd4b894aSbostic break;
439cd4b894aSbostic newilen -= s - newicap;
440cd4b894aSbostic newicap = s;
441cd4b894aSbostic
442cd4b894aSbostic /* make sure interpolated record is `:'-terminated */
443cd4b894aSbostic s += newilen;
444cd4b894aSbostic if (*(s-1) != ':') {
445cd4b894aSbostic *s = ':'; /* overwrite NUL with : */
446cd4b894aSbostic newilen++;
447cd4b894aSbostic }
448cd4b894aSbostic
449cd4b894aSbostic /*
450cd4b894aSbostic * Make sure there's enough room to insert the
451cd4b894aSbostic * new record.
452cd4b894aSbostic */
453cd4b894aSbostic diff = newilen - tclen;
454cd4b894aSbostic if (diff >= r_end - rp) {
455f6291f8aSelan u_int pos, tcpos, tcposend;
456f6291f8aSelan size_t newsize;
457cd4b894aSbostic
458cd4b894aSbostic pos = rp - record;
459cd4b894aSbostic newsize = r_end - record + diff + BFRAG;
460cd4b894aSbostic tcpos = tcstart - record;
461cd4b894aSbostic tcposend = tcend - record;
462cd4b894aSbostic record = realloc(record, newsize);
463cd4b894aSbostic if (record == NULL) {
464cd4b894aSbostic errno = ENOMEM;
465cd4b894aSbostic if (myfd)
466cd4b894aSbostic (void)close(fd);
467cd4b894aSbostic free(icap);
468cd4b894aSbostic return (-2);
469cd4b894aSbostic }
470cd4b894aSbostic r_end = record + newsize;
471cd4b894aSbostic rp = record + pos;
472cd4b894aSbostic tcstart = record + tcpos;
473cd4b894aSbostic tcend = record + tcposend;
474cd4b894aSbostic }
475cd4b894aSbostic
476cd4b894aSbostic /*
477cd4b894aSbostic * Insert tc'ed record into our record.
478cd4b894aSbostic */
479cd4b894aSbostic s = tcstart + newilen;
480cd4b894aSbostic bcopy(tcend, s, rp - tcend);
481cd4b894aSbostic bcopy(newicap, tcstart, newilen);
482cd4b894aSbostic rp += diff;
483cd4b894aSbostic free(icap);
484cd4b894aSbostic
485cd4b894aSbostic /*
486cd4b894aSbostic * Start scan on `:' so next cgetcap works properly
487cd4b894aSbostic * (cgetcap always skips first field).
488cd4b894aSbostic */
489cd4b894aSbostic scan = s-1;
490cd4b894aSbostic }
491cd4b894aSbostic
4925e4d7f0dSelan }
493cd4b894aSbostic /*
494cd4b894aSbostic * Close file (if we opened it), give back any extra memory, and
495cd4b894aSbostic * return capability, length and success.
496cd4b894aSbostic */
497cd4b894aSbostic if (myfd)
498cd4b894aSbostic (void)close(fd);
499cd4b894aSbostic *len = rp - record - 1; /* don't count NUL */
500cd4b894aSbostic if (r_end > rp)
5015157a406Selan if ((record =
5025157a406Selan realloc(record, (size_t)(rp - record))) == NULL) {
5035157a406Selan errno = ENOMEM;
5045157a406Selan return (-2);
5055157a406Selan }
5065157a406Selan
507cd4b894aSbostic *cap = record;
508f032f707Selan if (tc_not_resolved)
509f032f707Selan return (1);
510cd4b894aSbostic return (0);
511cd4b894aSbostic }
512cd4b894aSbostic
513f032f707Selan static int
cdbget(capdbp,bp,name)514f032f707Selan cdbget(capdbp, bp, name)
515f032f707Selan DB *capdbp;
516f032f707Selan char **bp, *name;
517f032f707Selan {
518f032f707Selan DBT key, data;
519f032f707Selan char *buf;
520f032f707Selan int st;
521f032f707Selan
522f032f707Selan key.data = name;
523f3be1ef2Sbostic key.size = strlen(name);
524f032f707Selan
525f3be1ef2Sbostic for (;;) {
526f3be1ef2Sbostic /* Get the reference. */
527f3be1ef2Sbostic switch(capdbp->get(capdbp, &key, &data, 0)) {
528f3be1ef2Sbostic case -1:
529f032f707Selan return (-2);
530f3be1ef2Sbostic case 1:
531f032f707Selan return (-1);
532f032f707Selan }
533f3be1ef2Sbostic
53419cbfad9Sbostic /* If not an index to another record, leave. */
535f3be1ef2Sbostic if (((char *)data.data)[0] != SHADOW)
536f3be1ef2Sbostic break;
537f3be1ef2Sbostic
53819cbfad9Sbostic key.data = (char *)data.data + 1;
539f3be1ef2Sbostic key.size = data.size - 1;
540f3be1ef2Sbostic }
541f3be1ef2Sbostic
54219cbfad9Sbostic *bp = (char *)data.data + 1;
54319cbfad9Sbostic return (((char *)(data.data))[0] == TCERR ? 1 : 0);
544f032f707Selan }
545f032f707Selan
546cd4b894aSbostic /*
547cd4b894aSbostic * Cgetmatch will return 0 if name is one of the names of the capability
548cd4b894aSbostic * record buf, -1 if not.
549cd4b894aSbostic */
550cd4b894aSbostic int
cgetmatch(buf,name)551cd4b894aSbostic cgetmatch(buf, name)
552cd4b894aSbostic char *buf, *name;
553cd4b894aSbostic {
554cd4b894aSbostic register char *np, *bp;
555cd4b894aSbostic
556cd4b894aSbostic /*
557cd4b894aSbostic * Start search at beginning of record.
558cd4b894aSbostic */
559cd4b894aSbostic bp = buf;
560cd4b894aSbostic for (;;) {
561cd4b894aSbostic /*
562cd4b894aSbostic * Try to match a record name.
563cd4b894aSbostic */
564cd4b894aSbostic np = name;
565cd4b894aSbostic for (;;)
566cd4b894aSbostic if (*np == '\0')
567cd4b894aSbostic if (*bp == '|' || *bp == ':' || *bp == '\0')
568cd4b894aSbostic return (0);
569cd4b894aSbostic else
570cd4b894aSbostic break;
571cd4b894aSbostic else
572cd4b894aSbostic if (*bp++ != *np++)
573cd4b894aSbostic break;
574cd4b894aSbostic
575cd4b894aSbostic /*
576cd4b894aSbostic * Match failed, skip to next name in record.
577cd4b894aSbostic */
578cd4b894aSbostic bp--; /* a '|' or ':' may have stopped the match */
579cd4b894aSbostic for (;;)
580cd4b894aSbostic if (*bp == '\0' || *bp == ':')
581cd4b894aSbostic return (-1); /* match failed totally */
582cd4b894aSbostic else
583cd4b894aSbostic if (*bp++ == '|')
584cd4b894aSbostic break; /* found next name */
585cd4b894aSbostic }
586cd4b894aSbostic }
587cd4b894aSbostic
588366565adSelan
589366565adSelan
590366565adSelan
591366565adSelan
592cd4b894aSbostic int
cgetfirst(buf,db_array)593cd4b894aSbostic cgetfirst(buf, db_array)
594cd4b894aSbostic char **buf, **db_array;
595cd4b894aSbostic {
596cd4b894aSbostic (void)cgetclose();
597cd4b894aSbostic return (cgetnext(buf, db_array));
598cd4b894aSbostic }
599cd4b894aSbostic
600cd4b894aSbostic static FILE *pfp;
601cd4b894aSbostic static int slash;
602cd4b894aSbostic static char **dbp;
603cd4b894aSbostic
604cd4b894aSbostic int
cgetclose()605cd4b894aSbostic cgetclose()
606cd4b894aSbostic {
607cd4b894aSbostic if (pfp != NULL) {
608cd4b894aSbostic (void)fclose(pfp);
609cd4b894aSbostic pfp = NULL;
610cd4b894aSbostic }
611cd4b894aSbostic dbp = NULL;
612f6291f8aSelan gottoprec = 0;
613cd4b894aSbostic slash = 0;
614cd4b894aSbostic return(0);
615cd4b894aSbostic }
616cd4b894aSbostic
617cd4b894aSbostic /*
618cd4b894aSbostic * Cgetnext() gets either the first or next entry in the logical database
619cd4b894aSbostic * specified by db_array. It returns 0 upon completion of the database, 1
620cd4b894aSbostic * upon returning an entry with more remaining, and -1 if an error occurs.
621cd4b894aSbostic */
622cd4b894aSbostic int
cgetnext(bp,db_array)623cd4b894aSbostic cgetnext(bp, db_array)
624cd4b894aSbostic register char **bp;
625cd4b894aSbostic char **db_array;
626cd4b894aSbostic {
627cd4b894aSbostic size_t len;
6285e4d7f0dSelan int status, i, done;
6295e4d7f0dSelan char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
630366565adSelan u_int dummy;
631cd4b894aSbostic
632366565adSelan if (dbp == NULL)
633cd4b894aSbostic dbp = db_array;
634366565adSelan
635f6291f8aSelan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
636f6291f8aSelan (void)cgetclose();
637cd4b894aSbostic return (-1);
638f6291f8aSelan }
639cd4b894aSbostic for(;;) {
640366565adSelan if (toprec && !gottoprec) {
641366565adSelan gottoprec = 1;
642366565adSelan line = toprec;
643366565adSelan } else {
6448edba266Sbostic line = fgetln(pfp, &len);
645366565adSelan if (line == NULL && pfp) {
646cd4b894aSbostic (void)fclose(pfp);
647cd4b894aSbostic if (ferror(pfp)) {
648f6291f8aSelan (void)cgetclose();
649cd4b894aSbostic return (-1);
650cd4b894aSbostic } else {
651f3be1ef2Sbostic if (*++dbp == NULL) {
652f6291f8aSelan (void)cgetclose();
653cd4b894aSbostic return (0);
654f3be1ef2Sbostic } else if ((pfp =
655f3be1ef2Sbostic fopen(*dbp, "r")) == NULL) {
656f6291f8aSelan (void)cgetclose();
657cd4b894aSbostic return (-1);
658cd4b894aSbostic } else
659cd4b894aSbostic continue;
660cd4b894aSbostic }
661b68e6496Selan } else
662b68e6496Selan line[len - 1] = '\0';
663f3be1ef2Sbostic if (len == 1) {
664f3be1ef2Sbostic slash = 0;
665f3be1ef2Sbostic continue;
666f3be1ef2Sbostic }
667f3be1ef2Sbostic if (isspace(*line) ||
668f3be1ef2Sbostic *line == ':' || *line == '#' || slash) {
669f3be1ef2Sbostic if (line[len - 2] == '\\')
670cd4b894aSbostic slash = 1;
671cd4b894aSbostic else
672cd4b894aSbostic slash = 0;
673cd4b894aSbostic continue;
674cd4b894aSbostic }
675f3be1ef2Sbostic if (line[len - 2] == '\\')
676cd4b894aSbostic slash = 1;
677cd4b894aSbostic else
678cd4b894aSbostic slash = 0;
679366565adSelan }
680cd4b894aSbostic
6815e4d7f0dSelan
6825e4d7f0dSelan /*
6835e4d7f0dSelan * Line points to a name line.
6845e4d7f0dSelan */
6855e4d7f0dSelan i = 0;
6865e4d7f0dSelan done = 0;
6875e4d7f0dSelan np = nbuf;
6885e4d7f0dSelan for (;;) {
6895e4d7f0dSelan for (cp = line; *cp != '\0'; cp++) {
6905e4d7f0dSelan if (*cp == ':') {
6915e4d7f0dSelan *np++ = ':';
6925e4d7f0dSelan done = 1;
6935e4d7f0dSelan break;
6945e4d7f0dSelan }
6955e4d7f0dSelan if (*cp == '\\')
6965e4d7f0dSelan break;
6975e4d7f0dSelan *np++ = *cp;
6985e4d7f0dSelan }
6995e4d7f0dSelan if (done) {
7005e4d7f0dSelan *np = '\0';
7015e4d7f0dSelan break;
7025e4d7f0dSelan } else { /* name field extends beyond the line */
7038edba266Sbostic line = fgetln(pfp, &len);
7045e4d7f0dSelan if (line == NULL && pfp) {
7055e4d7f0dSelan (void)fclose(pfp);
7065e4d7f0dSelan if (ferror(pfp)) {
7075e4d7f0dSelan (void)cgetclose();
7085e4d7f0dSelan return (-1);
7095e4d7f0dSelan }
7101ed17661Sbostic } else
711b68e6496Selan line[len - 1] = '\0';
7125e4d7f0dSelan }
7135e4d7f0dSelan }
714cd4b894aSbostic rp = buf;
7155e4d7f0dSelan for(cp = nbuf; *cp != NULL; cp++)
716cd4b894aSbostic if (*cp == '|' || *cp == ':')
717cd4b894aSbostic break;
718cd4b894aSbostic else
719cd4b894aSbostic *rp++ = *cp;
720cd4b894aSbostic
721cd4b894aSbostic *rp = '\0';
722d5255133Selan /*
723d5255133Selan * XXX
724d5255133Selan * Last argument of getent here should be nbuf if we want true
725d5255133Selan * sequential access in the case of duplicates.
726d5255133Selan * With NULL, getent will return the first entry found
727d5255133Selan * rather than the duplicate entry record. This is a
728d5255133Selan * matter of semantics that should be resolved.
729d5255133Selan */
730d5255133Selan status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
731f032f707Selan if (status == -2 || status == -3)
732f6291f8aSelan (void)cgetclose();
733f032f707Selan
734cd4b894aSbostic return (status + 1);
735cd4b894aSbostic }
736cd4b894aSbostic /* NOTREACHED */
737cd4b894aSbostic }
738cd4b894aSbostic
739cd4b894aSbostic /*
740cd4b894aSbostic * Cgetstr retrieves the value of the string capability cap from the
741cd4b894aSbostic * capability record pointed to by buf. A pointer to a decoded, NUL
742cd4b894aSbostic * terminated, malloc'd copy of the string is returned in the char *
743cd4b894aSbostic * pointed to by str. The length of the string not including the trailing
744cd4b894aSbostic * NUL is returned on success, -1 if the requested string capability
745cd4b894aSbostic * couldn't be found, -2 if a system error was encountered (storage
746cd4b894aSbostic * allocation failure).
747cd4b894aSbostic */
748cd4b894aSbostic int
cgetstr(buf,cap,str)749cd4b894aSbostic cgetstr(buf, cap, str)
750cd4b894aSbostic char *buf, *cap;
751cd4b894aSbostic char **str;
752cd4b894aSbostic {
753cd4b894aSbostic register u_int m_room;
754cd4b894aSbostic register char *bp, *mp;
755cd4b894aSbostic int len;
756cd4b894aSbostic char *mem;
757cd4b894aSbostic
758cd4b894aSbostic /*
759cd4b894aSbostic * Find string capability cap
760cd4b894aSbostic */
761cd4b894aSbostic bp = cgetcap(buf, cap, '=');
762cd4b894aSbostic if (bp == NULL)
763cd4b894aSbostic return (-1);
764cd4b894aSbostic
765cd4b894aSbostic /*
766cd4b894aSbostic * Conversion / storage allocation loop ... Allocate memory in
767cd4b894aSbostic * chunks SFRAG in size.
768cd4b894aSbostic */
769cd4b894aSbostic if ((mem = malloc(SFRAG)) == NULL) {
770cd4b894aSbostic errno = ENOMEM;
771cd4b894aSbostic return (-2); /* couldn't even allocate the first fragment */
772cd4b894aSbostic }
773cd4b894aSbostic m_room = SFRAG;
774cd4b894aSbostic mp = mem;
775cd4b894aSbostic
776cd4b894aSbostic while (*bp != ':' && *bp != '\0') {
777cd4b894aSbostic /*
778cd4b894aSbostic * Loop invariants:
779cd4b894aSbostic * There is always room for one more character in mem.
780cd4b894aSbostic * Mp always points just past last character in mem.
781cd4b894aSbostic * Bp always points at next character in buf.
782cd4b894aSbostic */
783cd4b894aSbostic if (*bp == '^') {
784cd4b894aSbostic bp++;
785cd4b894aSbostic if (*bp == ':' || *bp == '\0')
786cd4b894aSbostic break; /* drop unfinished escape */
787cd4b894aSbostic *mp++ = *bp++ & 037;
788cd4b894aSbostic } else if (*bp == '\\') {
789cd4b894aSbostic bp++;
790cd4b894aSbostic if (*bp == ':' || *bp == '\0')
791cd4b894aSbostic break; /* drop unfinished escape */
792cd4b894aSbostic if ('0' <= *bp && *bp <= '7') {
793cd4b894aSbostic register int n, i;
794cd4b894aSbostic
795cd4b894aSbostic n = 0;
796cd4b894aSbostic i = 3; /* maximum of three octal digits */
797cd4b894aSbostic do {
798cd4b894aSbostic n = n * 8 + (*bp++ - '0');
799cd4b894aSbostic } while (--i && '0' <= *bp && *bp <= '7');
800cd4b894aSbostic *mp++ = n;
801cd4b894aSbostic }
802cd4b894aSbostic else switch (*bp++) {
803cd4b894aSbostic case 'b': case 'B':
804cd4b894aSbostic *mp++ = '\b';
805cd4b894aSbostic break;
806cd4b894aSbostic case 't': case 'T':
807cd4b894aSbostic *mp++ = '\t';
808cd4b894aSbostic break;
809cd4b894aSbostic case 'n': case 'N':
810cd4b894aSbostic *mp++ = '\n';
811cd4b894aSbostic break;
812cd4b894aSbostic case 'f': case 'F':
813cd4b894aSbostic *mp++ = '\f';
814cd4b894aSbostic break;
815cd4b894aSbostic case 'r': case 'R':
816cd4b894aSbostic *mp++ = '\r';
817cd4b894aSbostic break;
818cd4b894aSbostic case 'e': case 'E':
819cd4b894aSbostic *mp++ = ESC;
820cd4b894aSbostic break;
821cd4b894aSbostic case 'c': case 'C':
822cd4b894aSbostic *mp++ = ':';
823cd4b894aSbostic break;
824cd4b894aSbostic default:
825cd4b894aSbostic /*
826cd4b894aSbostic * Catches '\', '^', and
827cd4b894aSbostic * everything else.
828cd4b894aSbostic */
829cd4b894aSbostic *mp++ = *(bp-1);
830cd4b894aSbostic break;
831cd4b894aSbostic }
832cd4b894aSbostic } else
833cd4b894aSbostic *mp++ = *bp++;
834cd4b894aSbostic m_room--;
835cd4b894aSbostic
836cd4b894aSbostic /*
837cd4b894aSbostic * Enforce loop invariant: if no room left in current
838cd4b894aSbostic * buffer, try to get some more.
839cd4b894aSbostic */
840cd4b894aSbostic if (m_room == 0) {
841f6291f8aSelan size_t size = mp - mem;
842cd4b894aSbostic
843cd4b894aSbostic if ((mem = realloc(mem, size + SFRAG)) == NULL)
844cd4b894aSbostic return (-2);
845cd4b894aSbostic m_room = SFRAG;
846cd4b894aSbostic mp = mem + size;
847cd4b894aSbostic }
848cd4b894aSbostic }
849cd4b894aSbostic *mp++ = '\0'; /* loop invariant let's us do this */
850cd4b894aSbostic m_room--;
851cd4b894aSbostic len = mp - mem - 1;
852cd4b894aSbostic
853cd4b894aSbostic /*
854cd4b894aSbostic * Give back any extra memory and return value and success.
855cd4b894aSbostic */
856cd4b894aSbostic if (m_room != 0)
8575157a406Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
8585157a406Selan return (-2);
859cd4b894aSbostic *str = mem;
860cd4b894aSbostic return (len);
861cd4b894aSbostic }
862cd4b894aSbostic
863cd4b894aSbostic /*
864cd4b894aSbostic * Cgetustr retrieves the value of the string capability cap from the
865cd4b894aSbostic * capability record pointed to by buf. The difference between cgetustr()
866cd4b894aSbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats
867cd4b894aSbostic * all characters literally. A pointer to a NUL terminated malloc'd
868cd4b894aSbostic * copy of the string is returned in the char pointed to by str. The
869cd4b894aSbostic * length of the string not including the trailing NUL is returned on success,
870cd4b894aSbostic * -1 if the requested string capability couldn't be found, -2 if a system
871cd4b894aSbostic * error was encountered (storage allocation failure).
872cd4b894aSbostic */
873cd4b894aSbostic int
cgetustr(buf,cap,str)874cd4b894aSbostic cgetustr(buf, cap, str)
875cd4b894aSbostic char *buf, *cap, **str;
876cd4b894aSbostic {
877cd4b894aSbostic register u_int m_room;
878cd4b894aSbostic register char *bp, *mp;
879cd4b894aSbostic int len;
880cd4b894aSbostic char *mem;
881cd4b894aSbostic
882cd4b894aSbostic /*
883cd4b894aSbostic * Find string capability cap
884cd4b894aSbostic */
885cd4b894aSbostic if ((bp = cgetcap(buf, cap, '=')) == NULL)
886cd4b894aSbostic return (-1);
887cd4b894aSbostic
888cd4b894aSbostic /*
889cd4b894aSbostic * Conversion / storage allocation loop ... Allocate memory in
890cd4b894aSbostic * chunks SFRAG in size.
891cd4b894aSbostic */
892cd4b894aSbostic if ((mem = malloc(SFRAG)) == NULL) {
893cd4b894aSbostic errno = ENOMEM;
894cd4b894aSbostic return (-2); /* couldn't even allocate the first fragment */
895cd4b894aSbostic }
896cd4b894aSbostic m_room = SFRAG;
897cd4b894aSbostic mp = mem;
898cd4b894aSbostic
899cd4b894aSbostic while (*bp != ':' && *bp != '\0') {
900cd4b894aSbostic /*
901cd4b894aSbostic * Loop invariants:
902cd4b894aSbostic * There is always room for one more character in mem.
903cd4b894aSbostic * Mp always points just past last character in mem.
904cd4b894aSbostic * Bp always points at next character in buf.
905cd4b894aSbostic */
906cd4b894aSbostic *mp++ = *bp++;
907cd4b894aSbostic m_room--;
908cd4b894aSbostic
909cd4b894aSbostic /*
910cd4b894aSbostic * Enforce loop invariant: if no room left in current
911cd4b894aSbostic * buffer, try to get some more.
912cd4b894aSbostic */
913cd4b894aSbostic if (m_room == 0) {
914f6291f8aSelan size_t size = mp - mem;
915cd4b894aSbostic
916cd4b894aSbostic if ((mem = realloc(mem, size + SFRAG)) == NULL)
917cd4b894aSbostic return (-2);
918cd4b894aSbostic m_room = SFRAG;
919cd4b894aSbostic mp = mem + size;
920cd4b894aSbostic }
921cd4b894aSbostic }
922cd4b894aSbostic *mp++ = '\0'; /* loop invariant let's us do this */
923cd4b894aSbostic m_room--;
924cd4b894aSbostic len = mp - mem - 1;
925cd4b894aSbostic
926cd4b894aSbostic /*
927cd4b894aSbostic * Give back any extra memory and return value and success.
928cd4b894aSbostic */
929cd4b894aSbostic if (m_room != 0)
9305157a406Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
9315157a406Selan return (-2);
932cd4b894aSbostic *str = mem;
933cd4b894aSbostic return (len);
934cd4b894aSbostic }
935cd4b894aSbostic
936cd4b894aSbostic /*
937cd4b894aSbostic * Cgetnum retrieves the value of the numeric capability cap from the
938cd4b894aSbostic * capability record pointed to by buf. The numeric value is returned in
939cd4b894aSbostic * the long pointed to by num. 0 is returned on success, -1 if the requested
940cd4b894aSbostic * numeric capability couldn't be found.
941cd4b894aSbostic */
942cd4b894aSbostic int
cgetnum(buf,cap,num)943cd4b894aSbostic cgetnum(buf, cap, num)
944cd4b894aSbostic char *buf, *cap;
945cd4b894aSbostic long *num;
946cd4b894aSbostic {
947cd4b894aSbostic register long n;
948cd4b894aSbostic register int base, digit;
949cd4b894aSbostic register char *bp;
950cd4b894aSbostic
951cd4b894aSbostic /*
952cd4b894aSbostic * Find numeric capability cap
953cd4b894aSbostic */
954cd4b894aSbostic bp = cgetcap(buf, cap, '#');
955cd4b894aSbostic if (bp == NULL)
956cd4b894aSbostic return (-1);
957cd4b894aSbostic
958cd4b894aSbostic /*
959cd4b894aSbostic * Look at value and determine numeric base:
960cd4b894aSbostic * 0x... or 0X... hexadecimal,
961cd4b894aSbostic * else 0... octal,
962cd4b894aSbostic * else decimal.
963cd4b894aSbostic */
964cd4b894aSbostic if (*bp == '0') {
965cd4b894aSbostic bp++;
966cd4b894aSbostic if (*bp == 'x' || *bp == 'X') {
967cd4b894aSbostic bp++;
968cd4b894aSbostic base = 16;
969cd4b894aSbostic } else
970cd4b894aSbostic base = 8;
971cd4b894aSbostic } else
972cd4b894aSbostic base = 10;
973cd4b894aSbostic
974cd4b894aSbostic /*
975cd4b894aSbostic * Conversion loop ...
976cd4b894aSbostic */
977cd4b894aSbostic n = 0;
978cd4b894aSbostic for (;;) {
979cd4b894aSbostic if ('0' <= *bp && *bp <= '9')
980cd4b894aSbostic digit = *bp - '0';
98188f3610bSralph else if ('a' <= *bp && *bp <= 'f')
982cd4b894aSbostic digit = 10 + *bp - 'a';
98388f3610bSralph else if ('A' <= *bp && *bp <= 'F')
98488f3610bSralph digit = 10 + *bp - 'A';
985cd4b894aSbostic else
986cd4b894aSbostic break;
987cd4b894aSbostic
988cd4b894aSbostic if (digit >= base)
989cd4b894aSbostic break;
990cd4b894aSbostic
991cd4b894aSbostic n = n * base + digit;
992cd4b894aSbostic bp++;
993cd4b894aSbostic }
994cd4b894aSbostic
995cd4b894aSbostic /*
996cd4b894aSbostic * Return value and success.
997cd4b894aSbostic */
998cd4b894aSbostic *num = n;
999cd4b894aSbostic return (0);
1000cd4b894aSbostic }
10015e4d7f0dSelan
10025e4d7f0dSelan
10035e4d7f0dSelan /*
10045e4d7f0dSelan * Compare name field of record.
10055e4d7f0dSelan */
10065e4d7f0dSelan static int
nfcmp(nf,rec)10075e4d7f0dSelan nfcmp(nf, rec)
10085e4d7f0dSelan char *nf, *rec;
10095e4d7f0dSelan {
10105e4d7f0dSelan char *cp, tmp;
10115e4d7f0dSelan int ret;
10125e4d7f0dSelan
10135e4d7f0dSelan for (cp = rec; *cp != ':'; cp++)
10145e4d7f0dSelan ;
10155e4d7f0dSelan
10165e4d7f0dSelan tmp = *(cp + 1);
10175e4d7f0dSelan *(cp + 1) = '\0';
10185e4d7f0dSelan ret = strcmp(nf, rec);
10195e4d7f0dSelan *(cp + 1) = tmp;
10205e4d7f0dSelan
10215e4d7f0dSelan return (ret);
10225e4d7f0dSelan }
1023