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