setvbuf.c revision 90792
1186690Sobrien/*
2186690Sobrien * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3186690Sobrien *      All rights reserved.
4186690Sobrien * Copyright (c) 1990, 1993
5234449Sobrien *	The Regents of the University of California.  All rights reserved.
6186690Sobrien *
7234449Sobrien * This code is derived from software contributed to Berkeley by
8234449Sobrien * Chris Torek.
9234449Sobrien *
10234449Sobrien * By using this file, you agree to the terms and conditions set
11186690Sobrien * forth in the LICENSE file which can be found at the top level of
12186690Sobrien * the sendmail distribution.
13186690Sobrien */
14186690Sobrien
15234449Sobrien#include <sm/gen.h>
16186690SobrienSM_RCSID("@(#)$Id: setvbuf.c,v 1.32 2001/09/11 04:04:49 gshapiro Exp $")
17186690Sobrien#include <stdlib.h>
18234449Sobrien#include <errno.h>
19234449Sobrien#include <fcntl.h>
20234449Sobrien#include <sm/io.h>
21234449Sobrien#include <sm/heap.h>
22234449Sobrien#include <sm/assert.h>
23186690Sobrien#include <sm/conf.h>
24186690Sobrien#include "local.h"
25186690Sobrien
26186690Sobrien/*
27186690Sobrien**  SM_IO_SETVBUF -- set the buffering type for a file
28186690Sobrien**
29186690Sobrien**  Set one of the different kinds of buffering, optionally including
30186690Sobrien**  a buffer.
31186690Sobrien**  If 'size' is == 0 then an "optimal" size will be selected.
32186690Sobrien**  If 'buf' is == NULL then space will be allocated at 'size'.
33186690Sobrien**
34186690Sobrien**	Parameters:
35186690Sobrien**		fp -- the file that buffering is to be changed for
36186690Sobrien**		timeout -- time allowed for completing the function
37186690Sobrien**		buf -- buffer to use
38186690Sobrien**		mode -- buffering method to use
39186690Sobrien**		size -- size of 'buf'
40186690Sobrien**
41186690Sobrien**	Returns:
42186690Sobrien**		Failure: SM_IO_EOF
43186690Sobrien**		Success: 0 (zero)
44186690Sobrien*/
45186690Sobrien
46186690Sobrienint
47186690Sobriensm_io_setvbuf(fp, timeout, buf, mode, size)
48186690Sobrien	SM_FILE_T *fp;
49186690Sobrien	int timeout;
50186690Sobrien	char *buf;
51234449Sobrien	int mode;
52186690Sobrien	size_t size;
53186690Sobrien{
54186690Sobrien	int ret, flags;
55186690Sobrien	size_t iosize;
56186690Sobrien	int ttyflag;
57186690Sobrien	int fd;
58186690Sobrien	struct timeval to;
59186690Sobrien
60186690Sobrien	SM_REQUIRE_ISA(fp, SmFileMagic);
61186690Sobrien
62234449Sobrien	/*
63186690Sobrien	**  Verify arguments.  The `int' limit on `size' is due to this
64186690Sobrien	**  particular implementation.  Note, buf and size are ignored
65234449Sobrien	**  when setting SM_IO_NBF.
66234449Sobrien	*/
67234449Sobrien
68234449Sobrien	if (mode != SM_IO_NBF)
69186690Sobrien		if ((mode != SM_IO_FBF && mode != SM_IO_LBF &&
70234449Sobrien		    mode != SM_IO_NOW) || (int) size < 0)
71234449Sobrien			return SM_IO_EOF;
72234449Sobrien
73234449Sobrien	/*
74234449Sobrien	**  Write current buffer, if any.  Discard unread input (including
75234449Sobrien	**  ungetc data), cancel line buffering, and free old buffer if
76234449Sobrien	**  malloc()ed.  We also clear any eof condition, as if this were
77234449Sobrien	**  a seek.
78186690Sobrien	*/
79186690Sobrien
80186690Sobrien	ret = 0;
81186690Sobrien	SM_CONVERT_TIME(fp, fd, timeout, &to);
82186690Sobrien	(void) sm_flush(fp, &timeout);
83186690Sobrien	if (HASUB(fp))
84186690Sobrien		FREEUB(fp);
85186690Sobrien	fp->f_r = fp->f_lbfsize = 0;
86234449Sobrien	flags = fp->f_flags;
87234449Sobrien	if (flags & SMMBF)
88234449Sobrien	{
89234449Sobrien		sm_free((void *) fp->f_bf.smb_base);
90234449Sobrien		fp->f_bf.smb_base = NULL;
91234449Sobrien	}
92234449Sobrien	flags &= ~(SMLBF | SMNBF | SMMBF | SMOPT | SMNPT | SMFEOF | SMNOW |
93234449Sobrien		   SMFBF);
94234449Sobrien
95234449Sobrien	/* If setting unbuffered mode, skip all the hard work. */
96186690Sobrien	if (mode == SM_IO_NBF)
97186690Sobrien		goto nbf;
98186690Sobrien
99234449Sobrien	/*
100234449Sobrien	**  Find optimal I/O size for seek optimization.  This also returns
101234449Sobrien	**  a `tty flag' to suggest that we check isatty(fd), but we do not
102186690Sobrien	**  care since our caller told us how to buffer.
103186690Sobrien	*/
104186690Sobrien
105186690Sobrien	flags |= sm_whatbuf(fp, &iosize, &ttyflag);
106186690Sobrien	if (size == 0)
107186690Sobrien	{
108186690Sobrien		buf = NULL;	/* force local allocation */
109186690Sobrien		size = iosize;
110186690Sobrien	}
111186690Sobrien
112186690Sobrien	/* Allocate buffer if needed. */
113186690Sobrien	if (buf == NULL)
114234449Sobrien	{
115186690Sobrien		if ((buf = sm_malloc(size)) == NULL)
116186690Sobrien		{
117186690Sobrien			/*
118186690Sobrien			**  Unable to honor user's request.  We will return
119234449Sobrien			**  failure, but try again with file system size.
120234449Sobrien			*/
121186690Sobrien
122186690Sobrien			ret = SM_IO_EOF;
123186690Sobrien			if (size != iosize)
124186690Sobrien			{
125186690Sobrien				size = iosize;
126186690Sobrien				buf = sm_malloc(size);
127234449Sobrien			}
128234449Sobrien		}
129234449Sobrien		if (buf == NULL)
130234449Sobrien		{
131234449Sobrien			/* No luck; switch to unbuffered I/O. */
132234449Sobriennbf:
133234449Sobrien			fp->f_flags = flags | SMNBF;
134234449Sobrien			fp->f_w = 0;
135234449Sobrien			fp->f_bf.smb_base = fp->f_p = fp->f_nbuf;
136234449Sobrien			fp->f_bf.smb_size = 1;
137234449Sobrien			return ret;
138234449Sobrien		}
139234449Sobrien		flags |= SMMBF;
140234449Sobrien	}
141186690Sobrien
142186690Sobrien	/*
143186690Sobrien	**  Kill any seek optimization if the buffer is not the
144234449Sobrien	**  right size.
145186690Sobrien	**
146186690Sobrien	**  SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)?
147234449Sobrien	*/
148234449Sobrien
149186690Sobrien	if (size != iosize)
150186690Sobrien		flags |= SMNPT;
151186690Sobrien
152186690Sobrien	/*
153186690Sobrien	**  Fix up the SM_FILE_T fields, and set sm_cleanup for output flush on
154186690Sobrien	**  exit (since we are buffered in some way).
155186690Sobrien	*/
156186690Sobrien
157186690Sobrien	if (mode == SM_IO_LBF)
158186690Sobrien		flags |= SMLBF;
159234449Sobrien	else if (mode == SM_IO_NOW)
160234449Sobrien		flags |= SMNOW;
161234449Sobrien	else if (mode == SM_IO_FBF)
162234449Sobrien		flags |= SMFBF;
163186690Sobrien	fp->f_flags = flags;
164234449Sobrien	fp->f_bf.smb_base = fp->f_p = (unsigned char *)buf;
165234449Sobrien	fp->f_bf.smb_size = size;
166234449Sobrien	/* fp->f_lbfsize is still 0 */
167234449Sobrien	if (flags & SMWR)
168234449Sobrien	{
169234449Sobrien		/*
170234449Sobrien		**  Begin or continue writing: see sm_wsetup().  Note
171234449Sobrien		**  that SMNBF is impossible (it was handled earlier).
172234449Sobrien		*/
173234449Sobrien
174234449Sobrien		if (flags & SMLBF)
175234449Sobrien		{
176234449Sobrien			fp->f_w = 0;
177234449Sobrien			fp->f_lbfsize = -fp->f_bf.smb_size;
178234449Sobrien		}
179234449Sobrien		else
180234449Sobrien			fp->f_w = size;
181234449Sobrien	}
182234449Sobrien	else
183234449Sobrien	{
184234449Sobrien		/* begin/continue reading, or stay in intermediate state */
185234449Sobrien		fp->f_w = 0;
186234449Sobrien	}
187234449Sobrien
188234449Sobrien	atexit(sm_cleanup);
189234449Sobrien	return ret;
190234449Sobrien}
191234449Sobrien