1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1989 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26/*      Copyright (c) 1984 AT&T */
27/*        All Rights Reserved   */
28
29#pragma ident	"%Z%%M%	%I%	%E% SMI"
30
31/*LINTLIBRARY*/
32#include <stdio.h>
33#include "../common/stdiom.h"
34#include <errno.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <malloc.h>
38#include <unistd.h>
39
40extern int	fclose();
41extern unsigned char (*_smbuf)[_SBFSIZ];
42
43void	_findbuf(FILE *);
44void	_bufsync(FILE *);
45
46/*
47 * Flush buffers on exit
48 */
49
50void
51_cleanup(void)
52{
53
54	_fwalk(fclose);
55}
56/*
57 *	fclose() will flush (output) buffers for a buffered open
58 *	FILE and then issue a system close on the _fileno.  The
59 *	_base field will be reset to NULL for any but stdin and
60 *	stdout, the _ptr field will be set the same as the _base
61 *	field. The _flags and the _cnt field will be zeroed.
62 *	If buffers had been obtained via malloc(), the space will
63 *	be free()'d.  In case the FILE was not open, or fflush()
64 *	or close() failed, an EOF will be returned, otherwise the
65 *	return value is 0.
66 */
67
68int
69fclose(FILE *iop)
70{
71	int rtn=EOF;
72
73	if(iop == NULL)
74		return(rtn);
75	if(iop->_flag & (_IOREAD | _IOWRT | _IORW)
76	   && (iop->_flag & _IOSTRG) == 0) {
77		rtn = (iop->_flag & _IONBF)? 0: fflush(iop);
78		if(close(fileno(iop)) < 0)
79			rtn = EOF;
80	}
81	if(iop->_flag & _IOMYBUF) {
82		free((char*)iop->_base);
83		iop->_base = NULL;
84	}
85	iop->_flag = 0;
86	iop->_cnt = 0;
87	iop->_ptr = iop->_base;
88	iop->_bufsiz = 0;
89	return(rtn);
90}
91
92/*
93 *	The fflush() routine must take care because of the
94 *	possibility for recursion. The calling program might
95 *	do IO in an interupt catching routine that is likely
96 *	to interupt the write() call within fflush()
97 */
98
99int
100fflush(FILE *iop)
101{
102	if (!(iop->_flag & _IOWRT)) {
103		return(0);
104	}
105	while(!(iop->_flag & _IONBF) && (iop->_flag & _IOWRT) &&
106			(iop->_base != NULL) && (iop->_ptr > iop->_base) )
107		(void) _xflsbuf(iop);
108	return(ferror(iop) ? EOF : 0);
109}
110
111/* The routine _flsbuf may or may not actually flush the output buffer.  If
112 * the file is line-buffered, the fact that iop->_cnt has run below zero
113 * is meaningless: it is always kept below zero so that invocations of putc
114 * will consistently give control to _flsbuf, even if the buffer is far from
115 * full.  _flsbuf, on seeing the "line-buffered" flag, determines whether the
116 * buffer is actually full by comparing iop->_ptr to the end of the buffer
117 * iop->_base + iop->_bufsiz.  If it is full, or if an output line is
118 * completed (with a newline), the buffer is flushed.  (Note: the character
119 * argument to _flsbuf is not flushed with the current buffer if the buffer
120 * is actually full -- it goes into the buffer after flushing.)
121 */
122
123int
124_flsbuf(unsigned char c, FILE *iop)
125{
126    unsigned char c1;
127
128    do {
129	/* check for linebuffered with write perm, but no EOF */
130	if ( (iop->_flag & (_IOLBF | _IOWRT | _IOEOF)) == (_IOLBF | _IOWRT) ) {
131		if ( iop->_ptr >= iop->_base + iop->_bufsiz )  /* if buffer full, */
132			break;		    /* exit do-while, and flush buf. */
133		if ( (*iop->_ptr++ = c) != '\n' )
134			return(c);
135		return(_xflsbuf(iop) == EOF ? EOF : c);
136	}
137	/* write out an unbuffered file, if have write perm, but no EOF */
138	if ( (iop->_flag & (_IONBF | _IOWRT | _IOEOF)) == (_IONBF | _IOWRT) ) {
139		c1 = c;
140		iop->_cnt = 0;
141		if (write(fileno(iop), (char *) &c1, 1) == 1)
142			return(c);
143		iop->_flag |= _IOERR;
144		return(EOF);
145	}
146	/* The _wrtchk call is here rather than at the top of _flsbuf to re- */
147	/* duce overhead for line-buffered I/O under normal circumstances.  */
148
149	if (_WRTCHK(iop))			/* is writing legitimate? */
150		return(EOF);
151    } while ( (iop->_flag & (_IONBF | _IOLBF)) );
152
153
154    (void) _xflsbuf(iop);   /* full buffer:  flush buffer */
155    (void) putc((char) c, iop);  /* then put "c" in newly emptied buf */
156			/* (which, because of signals, may NOT be empty) */
157    return( ferror(iop) ? EOF : c);
158}
159
160/* The function _xflsbuf writes out the current contents of the output
161 * buffer delimited by iop->_base and iop->_ptr.
162 * iop->_cnt is reset appropriately, but its value on entry to _xflsbuf
163 * is ignored.
164 *
165 * The following code is not strictly correct.  If a signal is raised,
166 * invoking a signal-handler which generates output into the same buffer
167 * being flushed, a peculiar output sequence may result (for example,
168 * the output generated by the signal-handler may appear twice).  At
169 * present no means has been found to guarantee correct behavior without
170 * resorting to the disabling of signals, a means considered too expensive.
171 * For now the code has been written with the intent of reducing the
172 * probability of strange effects and, when they do occur, of confining
173 * the damage.  Except under extremely pathological circumstances, this
174 * code should be expected to respect buffer boundaries even in the face
175 * of interrupts and other signals.
176 */
177
178int
179_xflsbuf(FILE *iop)
180{
181	unsigned char *base;
182	int n;
183
184	n = iop->_ptr - (base = iop->_base);
185	iop->_ptr = base;
186	iop->_cnt = (iop->_flag &(_IONBF | _IOLBF)) ? 0 : iop->_bufsiz;
187	_BUFSYNC(iop);
188	if (n > 0 && n != write(fileno(iop),(char*)base,(unsigned)n) )  {
189		iop->_flag |= _IOERR;
190		return(EOF);
191	}
192	return(0);
193}
194
195/* The function _wrtchk checks to see whether it is legitimate to write
196 * to the specified device.  If it is, _wrtchk sets flags in iop->_flag for
197 * writing, assures presence of a buffer, and returns 0.  If writing is not
198 * legitimate, EOF is returned.
199 */
200
201int
202_wrtchk(FILE *iop)
203{
204	if ( (iop->_flag & (_IOWRT | _IOEOF)) != _IOWRT ) {
205		if (!(iop->_flag & (_IOWRT | _IORW)))
206			return(EOF);  /* bogus call--read-only file */
207		iop->_flag = iop->_flag & ~_IOEOF | _IOWRT; /* fix flags */
208	}
209	if (iop->_flag & _IOSTRG)
210		return(0);	/* not our business to monkey with buffers or counts */
211	if (iop->_base == NULL)    /* this is first I/O to file--get buffer */
212		_findbuf(iop);
213	if (iop->_ptr == iop->_base && !(iop->_flag & (_IONBF | _IOLBF)) )  {
214		iop->_cnt = iop->_bufsiz; /* first write since seek--set cnt */
215		_BUFSYNC(iop);
216	}
217	return(0);
218}
219
220/*
221 * _findbuf, called only when iop->_base == NULL, locates a predefined buffer
222 * or allocates a buffer using malloc.  If a buffer is obtained from malloc,
223 * the _IOMYBUF flag is set in iop->_flag.
224 */
225
226void
227_findbuf(FILE *iop)
228{
229	int fno = fileno(iop); /* file number */
230	struct stat statb;
231	int size;
232
233	/* allocate a small block for unbuffered, large for buffered */
234	if (iop->_flag & _IONBF)  {
235		iop->_base = _smbuf[fno];
236		iop->_bufsiz = _SBFSIZ;
237	}  else  {
238
239		if ( isatty(fno) ) {
240			iop->_flag |= _IOLBF;
241			size = 128;
242		} else {
243			if (fstat(fno, &statb) < 0)
244				size = BUFSIZ;
245			else {
246				if ((size = statb.st_blksize) <= 0)
247					size = BUFSIZ;
248			}
249		}
250		if ((iop->_base = (unsigned char *) malloc(size+8)) != NULL) {
251			/* if  we got a buffer */
252			iop->_flag |= _IOMYBUF;
253			iop->_bufsiz = size;
254		} else {
255			/* if no room for buffer, use small buffer */
256			iop->_base = _smbuf[fno];
257			iop->_bufsiz = _SBFSIZ;
258			iop->_flag &= ~_IOLBF;
259			iop->_flag |= _IONBF;
260		}
261	}
262	iop->_ptr = iop->_base;
263}
264
265/*
266 * The function _bufsync is called because interrupts and other signals
267 * which occur in between the decrementing of iop->_cnt and the incrementing
268 * of iop->_ptr, or in other contexts as well, may upset the synchronization
269 * of iop->_cnt and iop->ptr.  If this happens, calling _bufsync should
270 * resynchronize the two quantities (this is not always possible).  Resyn-
271 * chronization guarantees that putc invocations will not write beyond
272 * the end of the buffer.  Note that signals during _bufsync can cause
273 * _bufsync to do the wrong thing, but usually with benign effects.
274 */
275
276void
277_bufsync(FILE *iop)
278{
279	int spaceleft;
280	unsigned char *bufend = iop->_base + iop->_bufsiz;
281
282	if ((spaceleft = bufend - iop->_ptr) < 0)
283		iop->_ptr = bufend;
284	else if (spaceleft < iop->_cnt)
285		iop->_cnt = spaceleft;
286}
287