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_
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 #if _PACKAGE_ast && !defined(SFSETLINEMODE)
60 #define SFSETLINEMODE		1
61 #endif
62 
63 #if SFSETLINEMODE
64 
sfsetlinemode()65 static 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
sfsetbuf(Sfio_t * f,Void_t * buf,size_t size)105 Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)
106 #else
107 Void_t* sfsetbuf(f,buf,size)
108 Sfio_t*	f;	/* stream to be buffered */
109 Void_t*	buf;	/* new buffer */
110 size_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 */
343 setbuf:
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 
409 done:
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