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