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