xref: /onnv-gate/usr/src/lib/libc/port/stdio/flush.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
30*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include "synonyms.h"
34*0Sstevel@tonic-gate #include "mtlib.h"
35*0Sstevel@tonic-gate #include "file64.h"
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate #define	_iob	__iob
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate #include <sys/types.h>
40*0Sstevel@tonic-gate #include <stdlib.h>
41*0Sstevel@tonic-gate #include <stdio.h>
42*0Sstevel@tonic-gate #include <thread.h>
43*0Sstevel@tonic-gate #include <synch.h>
44*0Sstevel@tonic-gate #include <unistd.h>
45*0Sstevel@tonic-gate #include <string.h>
46*0Sstevel@tonic-gate #include "stdiom.h"
47*0Sstevel@tonic-gate #include <wchar.h>
48*0Sstevel@tonic-gate #include <sys/stat.h>
49*0Sstevel@tonic-gate #include <stddef.h>
50*0Sstevel@tonic-gate #include <errno.h>
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate #undef end
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate #define	FILE_ARY_SZ	8 /* a nice size for FILE array & end_buffer_ptrs */
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate #ifdef	_LP64
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate /*
59*0Sstevel@tonic-gate  * Macros to declare and loop over a fp or fp/xfp combo to
60*0Sstevel@tonic-gate  * avoid some of the _LP64 ifdef hell.
61*0Sstevel@tonic-gate  */
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate #define	FPDECL(fp)		FILE *fp
64*0Sstevel@tonic-gate #define	FIRSTFP(lp, fp)		fp = lp->iobp
65*0Sstevel@tonic-gate #define	NEXTFP(fp)		fp++
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate #define	xFILE			FILE
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate #else
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate #define	FPDECL(fp)		FILE *fp; xFILE *x##fp
72*0Sstevel@tonic-gate #define	FIRSTFP(lp, fp)		x##fp = lp->iobp; \
73*0Sstevel@tonic-gate 				fp = x##fp ? &x##fp->_iob : &_iob[0]
74*0Sstevel@tonic-gate #define	NEXTFP(fp)		(x##fp ? fp = &(++x##fp)->_iob : ++fp)
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate /* The extended 32-bit file structure for use in link buffers */
77*0Sstevel@tonic-gate typedef struct xFILE {
78*0Sstevel@tonic-gate 	FILE			_iob;		/* must be first! */
79*0Sstevel@tonic-gate 	struct xFILEdata	_xdat;
80*0Sstevel@tonic-gate } xFILE;
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate #define	xmagic			_xdat._magic
83*0Sstevel@tonic-gate #define	xend			_xdat._end
84*0Sstevel@tonic-gate #define	xlock			_xdat._lock
85*0Sstevel@tonic-gate #define	xstate			_xdat._state
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate #define	FILEx(fp)		((struct xFILE *)(uintptr_t)fp)
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate /*
90*0Sstevel@tonic-gate  * The magic number stored is actually the pointer scrambled with
91*0Sstevel@tonic-gate  * a magic number.  Pointers to data items live everywhere in memory
92*0Sstevel@tonic-gate  * so we scramble the pointer in order to avoid accidental collisions.
93*0Sstevel@tonic-gate  */
94*0Sstevel@tonic-gate #define	XFILEMAGIC		0x63687367
95*0Sstevel@tonic-gate #define	XMAGIC(xfp)		((uintptr_t)(xfp) ^ XFILEMAGIC)
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate #endif /* _LP64 */
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate struct _link_	/* manages a list of streams */
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	xFILE *iobp;		/* the array of (x)FILE's */
102*0Sstevel@tonic-gate 				/* NULL for the __first_link in ILP32 */
103*0Sstevel@tonic-gate 	int	niob;		/* length of the arrays */
104*0Sstevel@tonic-gate 	struct _link_	*next;	/* next in the list */
105*0Sstevel@tonic-gate };
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate /*
108*0Sstevel@tonic-gate  * With dynamic linking, iob may be in either the library or in the user's
109*0Sstevel@tonic-gate  * a.out, so the run time linker fixes up the first entry in __first_link at
110*0Sstevel@tonic-gate  * process startup time.
111*0Sstevel@tonic-gate  *
112*0Sstevel@tonic-gate  * In 32 bit processes, we don't have xFILE[FILE_ARY_SZ] but FILE[],
113*0Sstevel@tonic-gate  * and _xftab[] instead; this is denoted by having iobp set to NULL in
114*0Sstevel@tonic-gate  * 32 bit mode for the first link entry.
115*0Sstevel@tonic-gate  */
116*0Sstevel@tonic-gate struct _link_ __first_link =	/* first in linked list */
117*0Sstevel@tonic-gate {
118*0Sstevel@tonic-gate #if !defined(_LP64)
119*0Sstevel@tonic-gate 	NULL,
120*0Sstevel@tonic-gate #else
121*0Sstevel@tonic-gate 	&_iob[0],
122*0Sstevel@tonic-gate #endif
123*0Sstevel@tonic-gate 	_NFILE,
124*0Sstevel@tonic-gate 	NULL
125*0Sstevel@tonic-gate };
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate /*
128*0Sstevel@tonic-gate  * Information cached to speed up searches.  We remember where we
129*0Sstevel@tonic-gate  * last found a free FILE* and we remember whether we saw any fcloses
130*0Sstevel@tonic-gate  * in between.  We also count the number of chunks we allocated, see
131*0Sstevel@tonic-gate  * _findiop() for an explanation.
132*0Sstevel@tonic-gate  * These variables are all protected by _first_link_lock.
133*0Sstevel@tonic-gate  */
134*0Sstevel@tonic-gate static struct _link_ *lastlink = NULL;
135*0Sstevel@tonic-gate static int fcloses;
136*0Sstevel@tonic-gate static int nchunks;
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate static rwlock_t _first_link_lock = DEFAULTRWLOCK;
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate static int _fflush_u_iops(void);
141*0Sstevel@tonic-gate static FILE *getiop(FILE *, rmutex_t *, mbstate_t *);
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate #define	GETIOP(fp, lk, mb)	{FILE *ret; \
144*0Sstevel@tonic-gate 	if ((ret = getiop((fp), __threaded ? (lk) : NULL, (mb))) != NULL) { \
145*0Sstevel@tonic-gate 		if (__threaded) \
146*0Sstevel@tonic-gate 			(void) __rw_unlock(&_first_link_lock); \
147*0Sstevel@tonic-gate 		return (ret); \
148*0Sstevel@tonic-gate 	}; \
149*0Sstevel@tonic-gate 	}
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate /*
152*0Sstevel@tonic-gate  * All functions that understand the linked list of iob's follow.
153*0Sstevel@tonic-gate  */
154*0Sstevel@tonic-gate #pragma weak _cleanup = __cleanup
155*0Sstevel@tonic-gate void
156*0Sstevel@tonic-gate __cleanup(void)		/* called at process end to flush ouput streams */
157*0Sstevel@tonic-gate {
158*0Sstevel@tonic-gate 	(void) fflush(NULL);
159*0Sstevel@tonic-gate }
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate /*
162*0Sstevel@tonic-gate  * For fork1-safety (see libc_prepare_atfork(), etc).
163*0Sstevel@tonic-gate  */
164*0Sstevel@tonic-gate void
165*0Sstevel@tonic-gate stdio_locks()
166*0Sstevel@tonic-gate {
167*0Sstevel@tonic-gate 	(void) __rw_wrlock(&_first_link_lock);
168*0Sstevel@tonic-gate 	/*
169*0Sstevel@tonic-gate 	 * XXX: We should acquire all of the iob locks here.
170*0Sstevel@tonic-gate 	 */
171*0Sstevel@tonic-gate }
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate void
174*0Sstevel@tonic-gate stdio_unlocks()
175*0Sstevel@tonic-gate {
176*0Sstevel@tonic-gate 	/*
177*0Sstevel@tonic-gate 	 * XXX: We should release all of the iob locks here.
178*0Sstevel@tonic-gate 	 */
179*0Sstevel@tonic-gate 	(void) __rw_unlock(&_first_link_lock);
180*0Sstevel@tonic-gate }
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate void
183*0Sstevel@tonic-gate _flushlbf(void)		/* fflush() all line-buffered streams */
184*0Sstevel@tonic-gate {
185*0Sstevel@tonic-gate 	FPDECL(fp);
186*0Sstevel@tonic-gate 	int i;
187*0Sstevel@tonic-gate 	struct _link_ *lp;
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	if (__threaded)
190*0Sstevel@tonic-gate 		(void) __rw_rdlock(&_first_link_lock);
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 	lp = &__first_link;
193*0Sstevel@tonic-gate 	do {
194*0Sstevel@tonic-gate 		FIRSTFP(lp, fp);
195*0Sstevel@tonic-gate 		for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
196*0Sstevel@tonic-gate 			if ((fp->_flag & (_IOLBF | _IOWRT)) ==
197*0Sstevel@tonic-gate 			    (_IOLBF | _IOWRT))
198*0Sstevel@tonic-gate 				(void) _fflush_u(fp);
199*0Sstevel@tonic-gate 		}
200*0Sstevel@tonic-gate 	} while ((lp = lp->next) != NULL);
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	if (__threaded)
203*0Sstevel@tonic-gate 		(void) __rw_unlock(&_first_link_lock);
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate /* allocate an unused stream; NULL if cannot */
207*0Sstevel@tonic-gate FILE *
208*0Sstevel@tonic-gate _findiop(void)
209*0Sstevel@tonic-gate {
210*0Sstevel@tonic-gate 	struct _link_ *lp, **prev;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	/* used so there only needs to be one malloc() */
213*0Sstevel@tonic-gate #ifdef _LP64
214*0Sstevel@tonic-gate 	typedef	struct	{
215*0Sstevel@tonic-gate 		struct _link_	hdr;
216*0Sstevel@tonic-gate 		FILE	iob[FILE_ARY_SZ];
217*0Sstevel@tonic-gate 	} Pkg;
218*0Sstevel@tonic-gate #else
219*0Sstevel@tonic-gate 	typedef union {
220*0Sstevel@tonic-gate 		struct {				/* Normal */
221*0Sstevel@tonic-gate 			struct _link_	hdr;
222*0Sstevel@tonic-gate 			xFILE	iob[FILE_ARY_SZ];
223*0Sstevel@tonic-gate 		} Pkgn;
224*0Sstevel@tonic-gate 		struct {				/* Reversed */
225*0Sstevel@tonic-gate 			xFILE	iob[FILE_ARY_SZ];
226*0Sstevel@tonic-gate 			struct _link_	hdr;
227*0Sstevel@tonic-gate 		} Pkgr;
228*0Sstevel@tonic-gate 	} Pkg;
229*0Sstevel@tonic-gate 	uintptr_t delta;
230*0Sstevel@tonic-gate #endif
231*0Sstevel@tonic-gate 	Pkg *pkgp;
232*0Sstevel@tonic-gate 	struct _link_ *hdr;
233*0Sstevel@tonic-gate 	FPDECL(fp);
234*0Sstevel@tonic-gate 	int i;
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	if (__threaded)
237*0Sstevel@tonic-gate 		(void) __rw_wrlock(&_first_link_lock);
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	if (lastlink == NULL) {
240*0Sstevel@tonic-gate rescan:
241*0Sstevel@tonic-gate 		fcloses = 0;
242*0Sstevel@tonic-gate 		lastlink = &__first_link;
243*0Sstevel@tonic-gate 	}
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 	lp = lastlink;
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 	/*
248*0Sstevel@tonic-gate 	 * lock to make testing of fp->_flag == 0 and acquiring the fp atomic
249*0Sstevel@tonic-gate 	 * and for allocation of new links
250*0Sstevel@tonic-gate 	 * low contention expected on _findiop(), hence coarse locking.
251*0Sstevel@tonic-gate 	 * for finer granularity, use fp->_lock for allocating an iop
252*0Sstevel@tonic-gate 	 * and make the testing of lp->next and allocation of new link atomic
253*0Sstevel@tonic-gate 	 * using lp->_lock
254*0Sstevel@tonic-gate 	 */
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	do {
257*0Sstevel@tonic-gate 		prev = &lp->next;
258*0Sstevel@tonic-gate 		FIRSTFP(lp, fp);
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 		for (i = lp->niob; --i >= 0; NEXTFP(fp)) {
261*0Sstevel@tonic-gate #ifdef	_LP64
262*0Sstevel@tonic-gate 			GETIOP(fp, &fp->_lock, &fp->_state);
263*0Sstevel@tonic-gate #else
264*0Sstevel@tonic-gate 			GETIOP(fp,
265*0Sstevel@tonic-gate 			    xfp ? &xfp->xlock : &_xftab[IOPIND(fp)]._lock,
266*0Sstevel@tonic-gate 			    xfp ? &xfp->xstate : &_xftab[IOPIND(fp)]._state);
267*0Sstevel@tonic-gate #endif	/*	_LP64	*/
268*0Sstevel@tonic-gate 		}
269*0Sstevel@tonic-gate 	} while ((lastlink = lp = lp->next) != NULL);
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate 	/*
272*0Sstevel@tonic-gate 	 * If there was a sufficient number of  fcloses since we last started
273*0Sstevel@tonic-gate 	 * at __first_link, we rescan all fp's again.  We do not rescan for
274*0Sstevel@tonic-gate 	 * all fcloses; that would simplify the algorithm but would make
275*0Sstevel@tonic-gate 	 * search times near O(n) again.
276*0Sstevel@tonic-gate 	 * Worst case behaviour would still be pretty bad (open a full set,
277*0Sstevel@tonic-gate 	 * then continously opening and closing one FILE * gets you a full
278*0Sstevel@tonic-gate 	 * scan each time).  That's why we over allocate 1 FILE for each
279*0Sstevel@tonic-gate 	 * 32 chunks.  More over allocation is better; this is a nice
280*0Sstevel@tonic-gate 	 * empirical value which doesn't cost a lot of memory, doesn't
281*0Sstevel@tonic-gate 	 * overallocate until we reach 256 FILE *s and keeps the performance
282*0Sstevel@tonic-gate 	 * pretty close to the optimum.
283*0Sstevel@tonic-gate 	 */
284*0Sstevel@tonic-gate 	if (fcloses > nchunks/32)
285*0Sstevel@tonic-gate 		goto rescan;
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	/*
288*0Sstevel@tonic-gate 	 * Need to allocate another and put it in the linked list.
289*0Sstevel@tonic-gate 	 */
290*0Sstevel@tonic-gate 	if ((pkgp = malloc(sizeof (Pkg))) == NULL) {
291*0Sstevel@tonic-gate 		if (__threaded)
292*0Sstevel@tonic-gate 			(void) __rw_unlock(&_first_link_lock);
293*0Sstevel@tonic-gate 		return (NULL);
294*0Sstevel@tonic-gate 	}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	(void) memset(pkgp, 0, sizeof (Pkg));
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate #ifdef _LP64
299*0Sstevel@tonic-gate 	hdr = &pkgp->hdr;
300*0Sstevel@tonic-gate 	hdr->iobp = &pkgp->iob[0];
301*0Sstevel@tonic-gate #else
302*0Sstevel@tonic-gate 	/*
303*0Sstevel@tonic-gate 	 * The problem with referencing a word after a FILE* is the possibility
304*0Sstevel@tonic-gate 	 * of a SIGSEGV if a non-stdio issue FILE structure ends on a page
305*0Sstevel@tonic-gate 	 * boundary.  We run this check so we never need to run an expensive
306*0Sstevel@tonic-gate 	 * check like mincore() in order to know whether it is
307*0Sstevel@tonic-gate 	 * safe to dereference ((xFILE*)fp)->xmagic.
308*0Sstevel@tonic-gate 	 * We allocate the block with two alternative layouts; if one
309*0Sstevel@tonic-gate 	 * layout is not properly aligned for our purposes, the other layout
310*0Sstevel@tonic-gate 	 * will be because the size of _link_ is small compared to
311*0Sstevel@tonic-gate 	 * sizeof (xFILE).
312*0Sstevel@tonic-gate 	 * The check performed is this:
313*0Sstevel@tonic-gate 	 *	If the distance from pkgp to the end of the page is
314*0Sstevel@tonic-gate 	 *	less than the the offset of the last xmagic field in the
315*0Sstevel@tonic-gate 	 *	xFILE structure, (the 0x1000 boundary is inside our just
316*0Sstevel@tonic-gate 	 *	allocated structure) and the distance modulo the size of xFILE
317*0Sstevel@tonic-gate 	 *	is identical to the offset of the first xmagic in the
318*0Sstevel@tonic-gate 	 *	structure (i.e., XXXXXX000 points to an xmagic field),
319*0Sstevel@tonic-gate 	 *	we need to use the reverse structure.
320*0Sstevel@tonic-gate 	 */
321*0Sstevel@tonic-gate 	if ((delta = 0x1000 - ((uintptr_t)pkgp & 0xfff)) <=
322*0Sstevel@tonic-gate 				offsetof(Pkg, Pkgn.iob[FILE_ARY_SZ-1].xmagic) &&
323*0Sstevel@tonic-gate 	    delta % sizeof (struct xFILE) ==
324*0Sstevel@tonic-gate 		    offsetof(Pkg, Pkgn.iob[0].xmagic)) {
325*0Sstevel@tonic-gate 		/* Use reversed structure */
326*0Sstevel@tonic-gate 		hdr = &pkgp->Pkgr.hdr;
327*0Sstevel@tonic-gate 		hdr->iobp = &pkgp->Pkgr.iob[0];
328*0Sstevel@tonic-gate 	} else {
329*0Sstevel@tonic-gate 		/* Use normal structure */
330*0Sstevel@tonic-gate 		hdr = &pkgp->Pkgn.hdr;
331*0Sstevel@tonic-gate 		hdr->iobp = &pkgp->Pkgn.iob[0];
332*0Sstevel@tonic-gate 	}
333*0Sstevel@tonic-gate #endif /* _LP64 */
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 	hdr->niob = FILE_ARY_SZ;
336*0Sstevel@tonic-gate 	nchunks++;
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate #ifdef	_LP64
339*0Sstevel@tonic-gate 	fp = hdr->iobp;
340*0Sstevel@tonic-gate 	for (i = 0; i < FILE_ARY_SZ; i++)
341*0Sstevel@tonic-gate 		_private_mutex_init(&fp[i]._lock,
342*0Sstevel@tonic-gate 			USYNC_THREAD|LOCK_RECURSIVE, NULL);
343*0Sstevel@tonic-gate #else
344*0Sstevel@tonic-gate 	xfp = hdr->iobp;
345*0Sstevel@tonic-gate 	fp = &xfp->_iob;
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate 	for (i = 0; i < FILE_ARY_SZ; i++) {
348*0Sstevel@tonic-gate 		xfp[i].xmagic = XMAGIC(&xfp[i]);
349*0Sstevel@tonic-gate 		_private_mutex_init(&xfp[i].xlock,
350*0Sstevel@tonic-gate 			USYNC_THREAD|LOCK_RECURSIVE, NULL);
351*0Sstevel@tonic-gate 	}
352*0Sstevel@tonic-gate #endif	/*	_LP64	*/
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate 	lastlink = *prev = hdr;
355*0Sstevel@tonic-gate 	fp->_ptr = 0;
356*0Sstevel@tonic-gate 	fp->_base = 0;
357*0Sstevel@tonic-gate 	fp->_flag = 0377; /* claim the fp by setting low 8 bits */
358*0Sstevel@tonic-gate 	if (__threaded)
359*0Sstevel@tonic-gate 		(void) __rw_unlock(&_first_link_lock);
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate 	return (fp);
362*0Sstevel@tonic-gate }
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate static void
365*0Sstevel@tonic-gate isseekable(FILE *iop)
366*0Sstevel@tonic-gate {
367*0Sstevel@tonic-gate 	struct stat64 fstatbuf;
368*0Sstevel@tonic-gate 	int save_errno;
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	save_errno = errno;
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	if (fstat64(iop->_file, &fstatbuf) != 0) {
373*0Sstevel@tonic-gate 		/*
374*0Sstevel@tonic-gate 		 * when we don't know what it is we'll
375*0Sstevel@tonic-gate 		 * do the old behaviour and flush
376*0Sstevel@tonic-gate 		 * the stream
377*0Sstevel@tonic-gate 		 */
378*0Sstevel@tonic-gate 		SET_SEEKABLE(iop);
379*0Sstevel@tonic-gate 		errno = save_errno;
380*0Sstevel@tonic-gate 		return;
381*0Sstevel@tonic-gate 	}
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate 	/*
384*0Sstevel@tonic-gate 	 * check for what is non-SEEKABLE
385*0Sstevel@tonic-gate 	 * otherwise assume it's SEEKABLE so we get the old
386*0Sstevel@tonic-gate 	 * behaviour and flush the stream
387*0Sstevel@tonic-gate 	 */
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	if (S_ISFIFO(fstatbuf.st_mode) || S_ISCHR(fstatbuf.st_mode) ||
390*0Sstevel@tonic-gate 	    S_ISSOCK(fstatbuf.st_mode) || S_ISDOOR(fstatbuf.st_mode)) {
391*0Sstevel@tonic-gate 		CLEAR_SEEKABLE(iop);
392*0Sstevel@tonic-gate 	} else {
393*0Sstevel@tonic-gate 		SET_SEEKABLE(iop);
394*0Sstevel@tonic-gate 	}
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	errno = save_errno;
397*0Sstevel@tonic-gate }
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate #ifdef	_LP64
400*0Sstevel@tonic-gate void
401*0Sstevel@tonic-gate _setbufend(FILE *iop, Uchar *end)	/* set the end pointer for this iop */
402*0Sstevel@tonic-gate {
403*0Sstevel@tonic-gate 	iop->_end = end;
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	isseekable(iop);
406*0Sstevel@tonic-gate }
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate #undef _realbufend
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate Uchar *
411*0Sstevel@tonic-gate _realbufend(FILE *iop)		/* get the end pointer for this iop */
412*0Sstevel@tonic-gate {
413*0Sstevel@tonic-gate 	return (iop->_end);
414*0Sstevel@tonic-gate }
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate #else /* _LP64 */
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate /*
419*0Sstevel@tonic-gate  * Awkward functions not needed for the sane 64 bit environment.
420*0Sstevel@tonic-gate  */
421*0Sstevel@tonic-gate /*
422*0Sstevel@tonic-gate  * xmagic must not be aligned on a 4K boundary. We guarantee this in
423*0Sstevel@tonic-gate  * _findiop().
424*0Sstevel@tonic-gate  */
425*0Sstevel@tonic-gate #define	VALIDXFILE(xfp) \
426*0Sstevel@tonic-gate 	(((uintptr_t)&(xfp)->xmagic & 0xfff) && \
427*0Sstevel@tonic-gate 	    (xfp)->xmagic == XMAGIC(FILEx(xfp)))
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate static struct xFILEdata *
430*0Sstevel@tonic-gate getxfdat(FILE *iop)
431*0Sstevel@tonic-gate {
432*0Sstevel@tonic-gate 	if (STDIOP(iop))
433*0Sstevel@tonic-gate 		return (&_xftab[IOPIND(iop)]);
434*0Sstevel@tonic-gate 	else if (VALIDXFILE(FILEx(iop)))
435*0Sstevel@tonic-gate 		return (&FILEx(iop)->_xdat);
436*0Sstevel@tonic-gate 	else
437*0Sstevel@tonic-gate 		return (NULL);
438*0Sstevel@tonic-gate }
439*0Sstevel@tonic-gate 
440*0Sstevel@tonic-gate void
441*0Sstevel@tonic-gate _setbufend(FILE *iop, Uchar *end)	/* set the end pointer for this iop */
442*0Sstevel@tonic-gate {
443*0Sstevel@tonic-gate 	struct xFILEdata *dat = getxfdat(iop);
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	if (dat != NULL)
446*0Sstevel@tonic-gate 		dat->_end = end;
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 	isseekable(iop);
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 	/*
451*0Sstevel@tonic-gate 	 * For binary compatibility with user programs using the
452*0Sstevel@tonic-gate 	 * old _bufend macro.  This is *so* broken, fileno()
453*0Sstevel@tonic-gate 	 * is not the proper index.
454*0Sstevel@tonic-gate 	 */
455*0Sstevel@tonic-gate 	if (iop->_file < _NFILE)
456*0Sstevel@tonic-gate 		_bufendtab[iop->_file] = end;
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate }
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate Uchar *
461*0Sstevel@tonic-gate _realbufend(FILE *iop)		/* get the end pointer for this iop */
462*0Sstevel@tonic-gate {
463*0Sstevel@tonic-gate 	struct xFILEdata *dat = getxfdat(iop);
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	if (dat != NULL)
466*0Sstevel@tonic-gate 		return (dat->_end);
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate 	return (NULL);
469*0Sstevel@tonic-gate }
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate /*
472*0Sstevel@tonic-gate  * _reallock() is invoked in each stdio call through the IOB_LCK() macro,
473*0Sstevel@tonic-gate  * it is therefor extremely performance sensitive.  We get better performance
474*0Sstevel@tonic-gate  * by inlining the STDIOP check in IOB_LCK and inlining a custom version
475*0Sstevel@tonic-gate  * of getfxdat() here.
476*0Sstevel@tonic-gate  */
477*0Sstevel@tonic-gate rmutex_t *
478*0Sstevel@tonic-gate _reallock(FILE *iop)
479*0Sstevel@tonic-gate {
480*0Sstevel@tonic-gate 	if (VALIDXFILE(FILEx(iop)))
481*0Sstevel@tonic-gate 		return (&FILEx(iop)->xlock);
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 	return (NULL);
484*0Sstevel@tonic-gate }
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate #endif	/*	_LP64	*/
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate /* make sure _cnt, _ptr are correct */
489*0Sstevel@tonic-gate void
490*0Sstevel@tonic-gate _bufsync(FILE *iop, Uchar *bufend)
491*0Sstevel@tonic-gate {
492*0Sstevel@tonic-gate 	ssize_t spaceleft;
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate 	spaceleft = bufend - iop->_ptr;
495*0Sstevel@tonic-gate 	if (bufend < iop->_ptr) {
496*0Sstevel@tonic-gate 		iop->_ptr = bufend;
497*0Sstevel@tonic-gate 		iop->_cnt = 0;
498*0Sstevel@tonic-gate 	} else if (spaceleft < iop->_cnt)
499*0Sstevel@tonic-gate 		iop->_cnt = spaceleft;
500*0Sstevel@tonic-gate }
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate /* really write out current buffer contents */
503*0Sstevel@tonic-gate int
504*0Sstevel@tonic-gate _xflsbuf(FILE *iop)
505*0Sstevel@tonic-gate {
506*0Sstevel@tonic-gate 	ssize_t n;
507*0Sstevel@tonic-gate 	Uchar *base = iop->_base;
508*0Sstevel@tonic-gate 	Uchar *bufend;
509*0Sstevel@tonic-gate 	ssize_t num_wrote;
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 	/*
512*0Sstevel@tonic-gate 	 * Hopefully, be stable with respect to interrupts...
513*0Sstevel@tonic-gate 	 */
514*0Sstevel@tonic-gate 	n = iop->_ptr - base;
515*0Sstevel@tonic-gate 	iop->_ptr = base;
516*0Sstevel@tonic-gate 	bufend = _bufend(iop);
517*0Sstevel@tonic-gate 	if (iop->_flag & (_IOLBF | _IONBF))
518*0Sstevel@tonic-gate 		iop->_cnt = 0;		/* always go to a flush */
519*0Sstevel@tonic-gate 	else
520*0Sstevel@tonic-gate 		iop->_cnt = bufend - base;
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	if (_needsync(iop, bufend))	/* recover from interrupts */
523*0Sstevel@tonic-gate 		_bufsync(iop, bufend);
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 	if (n > 0) {
526*0Sstevel@tonic-gate 		while ((num_wrote =
527*0Sstevel@tonic-gate 			write(iop->_file, base, (size_t)n)) != n) {
528*0Sstevel@tonic-gate 			if (num_wrote <= 0) {
529*0Sstevel@tonic-gate 				iop->_flag |= _IOERR;
530*0Sstevel@tonic-gate 				return (EOF);
531*0Sstevel@tonic-gate 			}
532*0Sstevel@tonic-gate 			n -= num_wrote;
533*0Sstevel@tonic-gate 			base += num_wrote;
534*0Sstevel@tonic-gate 		}
535*0Sstevel@tonic-gate 	}
536*0Sstevel@tonic-gate 	return (0);
537*0Sstevel@tonic-gate }
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate /* flush (write) buffer */
540*0Sstevel@tonic-gate int
541*0Sstevel@tonic-gate fflush(FILE *iop)
542*0Sstevel@tonic-gate {
543*0Sstevel@tonic-gate 	int res;
544*0Sstevel@tonic-gate 	rmutex_t *lk;
545*0Sstevel@tonic-gate 
546*0Sstevel@tonic-gate 	if (iop) {
547*0Sstevel@tonic-gate 		FLOCKFILE(lk, iop);
548*0Sstevel@tonic-gate 		res = _fflush_u(iop);
549*0Sstevel@tonic-gate 		FUNLOCKFILE(lk);
550*0Sstevel@tonic-gate 	} else {
551*0Sstevel@tonic-gate 		res = _fflush_u_iops();		/* flush all iops */
552*0Sstevel@tonic-gate 	}
553*0Sstevel@tonic-gate 	return (res);
554*0Sstevel@tonic-gate }
555*0Sstevel@tonic-gate 
556*0Sstevel@tonic-gate static int
557*0Sstevel@tonic-gate _fflush_u_iops(void)		/* flush all buffers */
558*0Sstevel@tonic-gate {
559*0Sstevel@tonic-gate 	FPDECL(iop);
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	int i;
562*0Sstevel@tonic-gate 	struct _link_ *lp;
563*0Sstevel@tonic-gate 	int res = 0;
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 	if (__threaded)
566*0Sstevel@tonic-gate 		(void) __rw_rdlock(&_first_link_lock);
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	lp = &__first_link;
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate 	do {
571*0Sstevel@tonic-gate 		/*
572*0Sstevel@tonic-gate 		 * Don't grab the locks for these file pointers
573*0Sstevel@tonic-gate 		 * since they are supposed to be flushed anyway
574*0Sstevel@tonic-gate 		 * It could also be the case in which the 2nd
575*0Sstevel@tonic-gate 		 * portion (base and lock) are not initialized
576*0Sstevel@tonic-gate 		 */
577*0Sstevel@tonic-gate 		FIRSTFP(lp, iop);
578*0Sstevel@tonic-gate 		for (i = lp->niob; --i >= 0; NEXTFP(iop)) {
579*0Sstevel@tonic-gate 		    if (!(iop->_flag & _IONBF)) {
580*0Sstevel@tonic-gate 			/*
581*0Sstevel@tonic-gate 			 * don't need to worry about the _IORW case
582*0Sstevel@tonic-gate 			 * since the iop will also marked with _IOREAD
583*0Sstevel@tonic-gate 			 * or _IOWRT whichever we are really doing
584*0Sstevel@tonic-gate 			 */
585*0Sstevel@tonic-gate 			if (iop->_flag & _IOWRT) {    /* flush write buffers */
586*0Sstevel@tonic-gate 			    res |= _fflush_u(iop);
587*0Sstevel@tonic-gate 			} else if (iop->_flag & _IOREAD) {
588*0Sstevel@tonic-gate 				/*
589*0Sstevel@tonic-gate 				 * flush seekable read buffers
590*0Sstevel@tonic-gate 				 * don't flush non-seekable read buffers
591*0Sstevel@tonic-gate 				 */
592*0Sstevel@tonic-gate 			    if (GET_SEEKABLE(iop)) {
593*0Sstevel@tonic-gate 				res |= _fflush_u(iop);
594*0Sstevel@tonic-gate 			    }
595*0Sstevel@tonic-gate 			}
596*0Sstevel@tonic-gate 		    }
597*0Sstevel@tonic-gate 		}
598*0Sstevel@tonic-gate 	} while ((lp = lp->next) != NULL);
599*0Sstevel@tonic-gate 	if (__threaded)
600*0Sstevel@tonic-gate 		(void) __rw_unlock(&_first_link_lock);
601*0Sstevel@tonic-gate 	return (res);
602*0Sstevel@tonic-gate }
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate /* flush buffer */
605*0Sstevel@tonic-gate int
606*0Sstevel@tonic-gate _fflush_u(FILE *iop)
607*0Sstevel@tonic-gate {
608*0Sstevel@tonic-gate 	int res = 0;
609*0Sstevel@tonic-gate 
610*0Sstevel@tonic-gate 	/* this portion is always assumed locked */
611*0Sstevel@tonic-gate 	if (!(iop->_flag & _IOWRT)) {
612*0Sstevel@tonic-gate 		(void) lseek64(iop->_file, -iop->_cnt, SEEK_CUR);
613*0Sstevel@tonic-gate 		iop->_cnt = 0;
614*0Sstevel@tonic-gate 		/* needed for ungetc & multibyte pushbacks */
615*0Sstevel@tonic-gate 		iop->_ptr = iop->_base;
616*0Sstevel@tonic-gate 		if (iop->_flag & _IORW) {
617*0Sstevel@tonic-gate 			iop->_flag &= ~_IOREAD;
618*0Sstevel@tonic-gate 		}
619*0Sstevel@tonic-gate 		return (0);
620*0Sstevel@tonic-gate 	}
621*0Sstevel@tonic-gate 	if (iop->_base != NULL && iop->_ptr > iop->_base) {
622*0Sstevel@tonic-gate 		res = _xflsbuf(iop);
623*0Sstevel@tonic-gate 	}
624*0Sstevel@tonic-gate 	if (iop->_flag & _IORW) {
625*0Sstevel@tonic-gate 		iop->_flag &= ~_IOWRT;
626*0Sstevel@tonic-gate 		iop->_cnt = 0;
627*0Sstevel@tonic-gate 	}
628*0Sstevel@tonic-gate 	return (res);
629*0Sstevel@tonic-gate }
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate /* flush buffer and close stream */
632*0Sstevel@tonic-gate int
633*0Sstevel@tonic-gate fclose(FILE *iop)
634*0Sstevel@tonic-gate {
635*0Sstevel@tonic-gate 	int res = 0;
636*0Sstevel@tonic-gate 	rmutex_t *lk;
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 	if (iop == NULL) {
639*0Sstevel@tonic-gate 		return (EOF);		/* avoid passing zero to FLOCKFILE */
640*0Sstevel@tonic-gate 	}
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate 	FLOCKFILE(lk, iop);
643*0Sstevel@tonic-gate 	if (iop->_flag == 0) {
644*0Sstevel@tonic-gate 		FUNLOCKFILE(lk);
645*0Sstevel@tonic-gate 		return (EOF);
646*0Sstevel@tonic-gate 	}
647*0Sstevel@tonic-gate 	/* Is not unbuffered and opened for read and/or write ? */
648*0Sstevel@tonic-gate 	if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
649*0Sstevel@tonic-gate 		res = _fflush_u(iop);
650*0Sstevel@tonic-gate 	if (close(iop->_file) < 0)
651*0Sstevel@tonic-gate 		res = EOF;
652*0Sstevel@tonic-gate 	if (iop->_flag & _IOMYBUF) {
653*0Sstevel@tonic-gate 		(void) free((char *)iop->_base - PUSHBACK);
654*0Sstevel@tonic-gate 	}
655*0Sstevel@tonic-gate 	iop->_base = NULL;
656*0Sstevel@tonic-gate 	iop->_ptr = NULL;
657*0Sstevel@tonic-gate 	iop->_cnt = 0;
658*0Sstevel@tonic-gate 	iop->_flag = 0;			/* marks it as available */
659*0Sstevel@tonic-gate 	FUNLOCKFILE(lk);
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate 	if (__threaded)
662*0Sstevel@tonic-gate 		(void) __rw_wrlock(&_first_link_lock);
663*0Sstevel@tonic-gate 	fcloses++;
664*0Sstevel@tonic-gate 	if (__threaded)
665*0Sstevel@tonic-gate 		(void) __rw_unlock(&_first_link_lock);
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 	return (res);
668*0Sstevel@tonic-gate }
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate /* flush buffer, close fd but keep the stream used by freopen() */
671*0Sstevel@tonic-gate int
672*0Sstevel@tonic-gate close_fd(FILE *iop)
673*0Sstevel@tonic-gate {
674*0Sstevel@tonic-gate 	int res = 0;
675*0Sstevel@tonic-gate 	mbstate_t *mb;
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate 	if (iop == NULL || iop->_flag == 0)
678*0Sstevel@tonic-gate 		return (EOF);
679*0Sstevel@tonic-gate 	/* Is not unbuffered and opened for read and/or write ? */
680*0Sstevel@tonic-gate 	if (!(iop->_flag & _IONBF) && (iop->_flag & (_IOWRT | _IOREAD | _IORW)))
681*0Sstevel@tonic-gate 		res = _fflush_u(iop);
682*0Sstevel@tonic-gate 	if (close(iop->_file) < 0)
683*0Sstevel@tonic-gate 		res = EOF;
684*0Sstevel@tonic-gate 	if (iop->_flag & _IOMYBUF) {
685*0Sstevel@tonic-gate 		(void) free((char *)iop->_base - PUSHBACK);
686*0Sstevel@tonic-gate 	}
687*0Sstevel@tonic-gate 	iop->_base = NULL;
688*0Sstevel@tonic-gate 	iop->_ptr = NULL;
689*0Sstevel@tonic-gate 	mb = _getmbstate(iop);
690*0Sstevel@tonic-gate 	if (mb != NULL)
691*0Sstevel@tonic-gate 		(void) memset(mb, 0, sizeof (mbstate_t));
692*0Sstevel@tonic-gate 	iop->_cnt = 0;
693*0Sstevel@tonic-gate 	_setorientation(iop, _NO_MODE);
694*0Sstevel@tonic-gate 	return (res);
695*0Sstevel@tonic-gate }
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate static FILE *
698*0Sstevel@tonic-gate getiop(FILE *fp, rmutex_t *lk, mbstate_t *mb)
699*0Sstevel@tonic-gate {
700*0Sstevel@tonic-gate 	if (lk != NULL && rmutex_trylock(lk))
701*0Sstevel@tonic-gate 		return (NULL);	/* locked: fp in use */
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate 	if (fp->_flag == 0) {	/* unused */
704*0Sstevel@tonic-gate #ifndef	_LP64
705*0Sstevel@tonic-gate 		fp->__orientation = 0;
706*0Sstevel@tonic-gate #endif /* _LP64 */
707*0Sstevel@tonic-gate 		fp->_cnt = 0;
708*0Sstevel@tonic-gate 		fp->_ptr = NULL;
709*0Sstevel@tonic-gate 		fp->_base = NULL;
710*0Sstevel@tonic-gate 		fp->_flag = 0377;	/* claim the fp by setting low 8 bits */
711*0Sstevel@tonic-gate 		(void) memset(mb, 0, sizeof (mbstate_t));
712*0Sstevel@tonic-gate 		FUNLOCKFILE(lk);
713*0Sstevel@tonic-gate 		return (fp);
714*0Sstevel@tonic-gate 	}
715*0Sstevel@tonic-gate 	FUNLOCKFILE(lk);
716*0Sstevel@tonic-gate 	return (NULL);
717*0Sstevel@tonic-gate }
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate #ifndef	_LP64
720*0Sstevel@tonic-gate /*
721*0Sstevel@tonic-gate  * DESCRIPTION:
722*0Sstevel@tonic-gate  * This function gets the pointer to the mbstate_t structure associated
723*0Sstevel@tonic-gate  * with the specified iop.
724*0Sstevel@tonic-gate  *
725*0Sstevel@tonic-gate  * RETURNS:
726*0Sstevel@tonic-gate  * If the associated mbstate_t found, the pointer to the mbstate_t is
727*0Sstevel@tonic-gate  * returned.  Otherwise, NULL is returned.
728*0Sstevel@tonic-gate  */
729*0Sstevel@tonic-gate mbstate_t *
730*0Sstevel@tonic-gate _getmbstate(FILE *iop)
731*0Sstevel@tonic-gate {
732*0Sstevel@tonic-gate 	struct xFILEdata *dat = getxfdat(iop);
733*0Sstevel@tonic-gate 
734*0Sstevel@tonic-gate 	if (dat != NULL)
735*0Sstevel@tonic-gate 		return (&dat->_state);
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 	return (NULL);
738*0Sstevel@tonic-gate }
739*0Sstevel@tonic-gate #endif
740