1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#if defined(__STDPP__directive) && defined(__STDPP__hide)
23__STDPP__directive pragma pp:hide getpagesize
24#else
25#define getpagesize	______getpagesize
26#endif
27
28#include	"sfhdr.h"
29
30#if defined(__STDPP__directive) && defined(__STDPP__hide)
31__STDPP__directive pragma pp:nohide getpagesize
32#else
33#undef	getpagesize
34#endif
35
36#if _lib_getpagesize
37_BEGIN_EXTERNS_
38extern int	getpagesize _ARG_((void));
39_END_EXTERNS_
40#endif
41
42/*	Set a (new) buffer for a stream.
43**	If size < 0, it is assigned a suitable value depending on the
44**	kind of stream. The actual buffer size allocated is dependent
45**	on how much memory is available.
46**
47**	Written by Kiem-Phong Vo.
48*/
49
50#if !_sys_stat
51struct stat
52{	int	st_mode;
53	int	st_size;
54};
55#undef sysfstatf
56#define sysfstatf(fd,st)	(-1)
57#endif /*_sys_stat*/
58
59static int setlinemode()
60{	char*			astsfio;
61	char*			endw;
62
63	static int		modes = -1;
64	static const char	sf_line[] = "SF_LINE";
65	static const char	sf_wcwidth[] = "SF_WCWIDTH";
66
67#define ISSEPAR(c)	((c) == ',' || (c) == ' ' || (c) == '\t')
68	if (modes < 0)
69	{	modes = 0;
70		if(astsfio = getenv("_AST_SFIO_OPTIONS"))
71		{	for(; *astsfio != 0; astsfio = endw)
72			{	while(ISSEPAR(*astsfio) )
73					*astsfio++;
74				for(endw = astsfio; *endw && !ISSEPAR(*endw); ++endw)
75					;
76				if((endw-astsfio) == (sizeof(sf_line)-1) &&
77				   strncmp(astsfio,sf_line,endw-astsfio) == 0)
78				{	if ((modes |= SF_LINE) == (SF_LINE|SF_WCWIDTH))
79						break;
80				}
81				else if((endw-astsfio) == (sizeof(sf_wcwidth)-1) &&
82				   strncmp(astsfio,sf_wcwidth,endw-astsfio) == 0)
83				{	if ((modes |= SF_WCWIDTH) == (SF_LINE|SF_WCWIDTH))
84						break;
85				}
86			}
87		}
88	}
89	return modes;
90}
91
92#if __STD_C
93Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)
94#else
95Void_t* sfsetbuf(f,buf,size)
96Sfio_t*	f;	/* stream to be buffered */
97Void_t*	buf;	/* new buffer */
98size_t	size;	/* buffer size, -1 for default size */
99#endif
100{
101	int		sf_malloc, oflags, init, okmmap, local;
102	ssize_t		bufsize, blksz;
103	Sfdisc_t*	disc;
104	sfstat_t	st;
105	uchar*		obuf = NIL(uchar*);
106	ssize_t		osize = 0;
107	SFMTXDECL(f);
108
109	SFONCE();
110
111	SFMTXENTER(f,NIL(Void_t*));
112
113	GETLOCAL(f,local);
114
115	if(size == 0 && buf)
116	{	/* special case to get buffer info */
117		_Sfi = f->val = (f->bits&SF_MMAP) ? (f->endb-f->data) : f->size;
118		SFMTXRETURN(f, (Void_t*)f->data);
119	}
120
121	/* cleanup actions already done, don't allow write buffering any more */
122	if(_Sfexiting && !(f->flags&SF_STRING) && (f->mode&SF_WRITE))
123	{	buf = NIL(Void_t*);
124		size = 0;
125	}
126
127	if((init = f->mode&SF_INIT) )
128	{	if(!f->pool && _sfsetpool(f) < 0)
129			SFMTXRETURN(f, NIL(Void_t*));
130	}
131	else if((f->mode&SF_RDWR) != SFMODE(f,local) && _sfmode(f,0,local) < 0)
132		SFMTXRETURN(f, NIL(Void_t*));
133
134	if(init)
135		f->mode = (f->mode&SF_RDWR)|SF_LOCK;
136	else
137	{	int	rv;
138
139		/* make sure there is no hidden read data */
140		if(f->proc && (f->flags&SF_READ) && (f->mode&SF_WRITE) &&
141		   _sfmode(f,SF_READ,local) < 0)
142			SFMTXRETURN(f, NIL(Void_t*));
143
144		/* synchronize first */
145		SFLOCK(f,local); rv = SFSYNC(f); SFOPEN(f,local);
146		if(rv < 0)
147			SFMTXRETURN(f, NIL(Void_t*));
148
149		/* turn off the SF_SYNCED bit because buffer is changing */
150		f->mode &= ~SF_SYNCED;
151	}
152
153	SFLOCK(f,local);
154
155	if((Sfio_t*)buf != f)
156		blksz = -1;
157	else /* setting alignment size only */
158	{	blksz = (ssize_t)size;
159
160		if(!init) /* stream already initialized */
161		{	obuf = f->data;
162			osize = f->size;
163			goto done;
164		}
165		else /* initialize stream as if in the default case */
166		{	buf = NIL(Void_t*);
167			size = (size_t)SF_UNBOUND;
168		}
169	}
170
171	bufsize = 0;
172	oflags = f->flags;
173
174	/* see if memory mapping is possible (see sfwrite for SF_BOTH) */
175	okmmap = (buf || (f->flags&SF_STRING) || (f->flags&SF_RDWR) == SF_RDWR) ? 0 : 1;
176
177	/* save old buffer info */
178#ifdef MAP_TYPE
179	if(f->bits&SF_MMAP)
180	{	if(f->data)
181		{	SFMUNMAP(f,f->data,f->endb-f->data);
182			f->data = NIL(uchar*);
183		}
184	} else
185#endif
186	if(f->data == f->tiny)
187	{	f->data = NIL(uchar*);
188		f->size = 0;
189	}
190	obuf  = f->data;
191	osize = f->size;
192
193	f->flags &= ~SF_MALLOC;
194	f->bits  &= ~SF_MMAP;
195
196	/* pure read/string streams must have a valid string */
197	if((f->flags&(SF_RDWR|SF_STRING)) == SF_RDSTR &&
198	   (size == (size_t)SF_UNBOUND || !buf))
199		size = 0;
200
201	/* set disc to the first discipline with a seekf */
202	for(disc = f->disc; disc; disc = disc->disc)
203		if(disc->seekf)
204			break;
205
206	if((init || local) && !(f->flags&SF_STRING))
207	{	/* ASSERT(f->file >= 0) */
208		st.st_mode = 0;
209
210		/* if has discipline, set size by discipline if possible */
211		if(!_sys_stat || disc)
212		{	if((f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,disc)) < 0)
213				goto unseekable;
214			else
215			{	Sfoff_t	e;
216				if((e = SFSK(f,(Sfoff_t)0,SEEK_END,disc)) >= 0)
217					f->extent = e > f->here ? e : f->here;
218				(void)SFSK(f,f->here,SEEK_SET,disc);
219				goto setbuf;
220			}
221		}
222
223		/* get file descriptor status */
224		if(sysfstatf((int)f->file,&st) < 0)
225			f->here = -1;
226		else
227		{
228#if _sys_stat && _stat_blksize	/* preferred io block size */
229			f->blksz = (size_t)st.st_blksize;
230#endif
231			bufsize = 64 * 1024;
232			if(S_ISDIR(st.st_mode) || (Sfoff_t)st.st_size < (Sfoff_t)SF_GRAIN)
233				okmmap = 0;
234			if(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
235				f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,f->disc);
236			else	f->here = -1;
237
238#if O_TEXT /* no memory mapping with O_TEXT because read()/write() alter data stream */
239			if(okmmap && f->here >= 0 &&
240			   (sysfcntlf((int)f->file,F_GETFL,0) & O_TEXT) )
241				okmmap = 0;
242#endif
243		}
244
245		if(init)
246			f->flags |= setlinemode();
247
248		if(f->here >= 0)
249		{	f->extent = (Sfoff_t)st.st_size;
250
251			/* seekable std-devices are share-public by default */
252			if(f == sfstdin || f == sfstdout || f == sfstderr)
253				f->flags |= SF_SHARE|SF_PUBLIC;
254		}
255		else
256		{
257		unseekable:
258			f->extent = -1;
259			f->here = 0;
260
261			if(init)
262			{	if(S_ISCHR(st.st_mode) )
263				{	int oerrno = errno;
264
265					bufsize = SF_GRAIN;
266
267					/* set line mode for terminals */
268					if(!(f->flags&(SF_LINE|SF_WCWIDTH)) && isatty(f->file))
269						f->flags |= SF_LINE|SF_WCWIDTH;
270#if _sys_stat
271					else	/* special case /dev/null */
272					{	reg int	dev, ino;
273						dev = (int)st.st_dev;
274						ino = (int)st.st_ino;
275						if(sysstatf(DEVNULL,&st) >= 0 &&
276						   dev == (int)st.st_dev &&
277						   ino == (int)st.st_ino)
278							SFSETNULL(f);
279					}
280#endif
281					errno = oerrno;
282				}
283
284				/* initialize side buffer for r+w unseekable streams */
285				if(!f->proc && (f->bits&SF_BOTH) )
286					(void)_sfpopen(f,-1,-1,1);
287			}
288		}
289
290		/* set page size, this is also the desired default buffer size */
291		if(_Sfpage <= 0)
292		{
293#if _lib_getpagesize
294			if((_Sfpage = (size_t)getpagesize()) <= 0)
295#endif
296				_Sfpage = SF_PAGE;
297		}
298	}
299
300#ifdef MAP_TYPE
301	if(okmmap && size && (f->mode&SF_READ) && f->extent >= 0 )
302	{	/* see if we can try memory mapping */
303		if(!disc)
304			for(disc = f->disc; disc; disc = disc->disc)
305				if(disc->readf)
306					break;
307		if(!disc)
308		{	f->bits |= SF_MMAP;
309			if(size == (size_t)SF_UNBOUND)
310			{	if(bufsize > _Sfpage)
311					size = bufsize * SF_NMAP;
312				else	size = _Sfpage * SF_NMAP;
313				if(size > 256*1024)
314					size = 256*1024;
315			}
316		}
317	}
318#endif
319
320	/* get buffer space */
321setbuf:
322	if(size == (size_t)SF_UNBOUND)
323	{	/* define a default size suitable for block transfer */
324		if(init && osize > 0)
325			size = osize;
326		else if(f == sfstderr && (f->mode&SF_WRITE))
327			size = 0;
328		else if(f->flags&SF_STRING )
329			size = SF_GRAIN;
330		else if((f->flags&SF_READ) && !(f->bits&SF_BOTH) &&
331			f->extent > 0 && f->extent < (Sfoff_t)_Sfpage )
332			size = (((size_t)f->extent + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN;
333		else if((ssize_t)(size = _Sfpage) < bufsize)
334			size = bufsize;
335
336		buf = NIL(Void_t*);
337	}
338
339	sf_malloc = 0;
340	if(size > 0 && !buf && !(f->bits&SF_MMAP))
341	{	/* try to allocate a buffer */
342		if(obuf && size == (size_t)osize && init)
343		{	buf = (Void_t*)obuf;
344			obuf = NIL(uchar*);
345			sf_malloc = (oflags&SF_MALLOC);
346		}
347		if(!buf)
348		{	/* do allocation */
349			while(!buf && size > 0)
350			{	if((buf = (Void_t*)malloc(size)) )
351					break;
352				else	size /= 2;
353			}
354			if(size > 0)
355				sf_malloc = SF_MALLOC;
356		}
357	}
358
359	if(size == 0 && !(f->flags&SF_STRING) && !(f->bits&SF_MMAP) && (f->mode&SF_READ))
360	{	/* use the internal buffer */
361		size = sizeof(f->tiny);
362		buf = (Void_t*)f->tiny;
363	}
364
365	/* set up new buffer */
366	f->size = size;
367	f->next = f->data = f->endr = f->endw = (uchar*)buf;
368	f->endb = (f->mode&SF_READ) ? f->data : f->data+size;
369	if(f->flags&SF_STRING)
370	{	/* these fields are used to test actual size - see sfseek() */
371		f->extent = (!sf_malloc &&
372			     ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ) ? size : 0;
373		f->here = 0;
374
375		/* read+string stream should have all data available */
376		if((f->mode&SF_READ) && !sf_malloc)
377			f->endb = f->data+size;
378	}
379
380	f->flags = (f->flags & ~SF_MALLOC)|sf_malloc;
381
382	if(obuf && obuf != f->data && osize > 0 && (oflags&SF_MALLOC))
383	{	free((Void_t*)obuf);
384		obuf = NIL(uchar*);
385	}
386
387done:
388	_Sfi = f->val = obuf ? osize : 0;
389
390	/* blksz is used for aligning disk block boundary while reading data to
391	** optimize data transfer from disk (eg, via direct I/O). blksz can be
392	** at most f->size/2 so that data movement in buffer can be optimized.
393	** blksz should also be a power-of-2 for optimal disk seeks.
394	*/
395	if(blksz <= 0 || (blksz & (blksz-1)) != 0 )
396		blksz = SF_GRAIN;
397	while(blksz > f->size/2)
398		blksz /= 2;
399	f->blksz = blksz;
400
401	SFOPEN(f,local);
402
403	SFMTXRETURN(f, (Void_t*)obuf);
404}
405