1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2008 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_
38 extern 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
51 struct 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 static 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
93 Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)
94 #else
95 Void_t* sfsetbuf(f,buf,size)
96 Sfio_t*	f;	/* stream to be buffered */
97 Void_t*	buf;	/* new buffer */
98 size_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 */
321 setbuf:
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 
387 done:
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