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