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