1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2013 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 <glenn.s.fowler@gmail.com>                *
18 *                    David Korn <dgkorn@gmail.com>                     *
19 *                     Phong Vo <phongvo@gmail.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 
65 #ifndef roundof
66 #define roundof(x,y)		(((x)+(y)-1)&~((y)-1))
67 #endif
68 
69 #define SF_TEST_read		0x01000
70 
sfsetlinemode(void)71 static int sfsetlinemode(void)
72 {	char*			s;
73 	char*			t;
74 	char*			v;
75 	unsigned long		b;
76 	size_t			z;
77 	int			n;
78 	int			m;
79 	char			buf[1024];
80 
81 	static int		modes = -1;
82 
83 	if(modes < 0)
84 	{
85 		modes = 0;
86 		/* modeled after the VMALLOC_OPTIONS parser -- lax on the syntax */
87 		if((t = getenv("SFIO_OPTIONS")) && t[0])
88 		{	/* copy option string to a writable buffer */
89 			for(s = &buf[0], v = &buf[sizeof(buf)-1]; s < v; ++s)
90 				if((*s = *t++) == 0 )
91 					break;
92 			*s = 0;
93 
94 			for(s = buf;; )
95 			{	/* skip blanks to option name */
96 				while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' || *s == ',')
97 					s++;
98 				if (*(t = s) == 0)
99 					break;
100 
101 				v = NIL(char*);
102 				while (*s)
103 				{	if (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' || *s == ',')
104 					{	*s++ = 0; /* end of name */
105 						break;
106 					}
107 					else if (!v && *s == '=')
108 					{	*s++ = 0; /* end of name */
109 						if (*(v = s) == 0)
110 							v = NIL(char*);
111 					}
112 					else	s++;
113 				}
114 				if((t[0] == 'n' || t[0] == 'N') && (t[1] == 'o' || t[1] == 'O'))
115 				{	t += 2;
116 					n = 0;
117 				}
118 				else	n = 1;
119 				if(t[0] == 'S' && t[1] == 'F')
120 				{	t += 2;
121 					if(t[0] == '_')
122 						t += 1;
123 				}
124 				switch (t[0])
125 				{
126 				case 'L':		/* SF_LINE */
127 				case 'l':
128 					if(n)
129 						modes |= SF_LINE;
130 					else
131 						modes &= ~SF_LINE;
132 					break;
133 				case 'M':		/* maxrec maxmap */
134 				case 'm':
135 					if(n && v)
136 #if _PACKAGE_ast
137 						z = (size_t)strtonll(v, NiL, NiL, 0);
138 #else
139 						z = (size_t)strtol(v, NiL, 0);
140 #endif
141 					else
142 						z = 0;
143 					for(;;)
144 					{	switch(*++t)
145 						{
146 						case 0:
147 							break;
148 						case 'M':	/* max map */
149 						case 'm':
150 							if(z)
151 							{
152 								if(z != (size_t)SF_UNBOUND)
153 									z = roundof(z, _Sfpage);
154 								_Sfmaxm = z;
155 								_Sftest &= ~SF_TEST_read;
156 							}
157 							else
158 								_Sftest |= SF_TEST_read;
159 							break;
160 						case 'R':	/* max rec */
161 						case 'r':
162 							_Sfmaxr = z;
163 							break;
164 						default:
165 							continue;
166 						}
167 						break;
168 					}
169 					break;
170 				case 'T':
171 				case 't':
172 					if(v)
173 						do
174 						{
175 							if (v[0] == 'n' && v[1] == 'o')
176 							{
177 								v += 2;
178 								m = !n;
179 							}
180 							else
181 								m = n;
182 							switch (v[0])
183 							{
184 							default:
185 								if(isdigit(v[0]))
186 								{
187 #if _PACKAGE_ast
188 									b = strtonll(v, NiL, NiL, 0);
189 #else
190 									b = strtoul(v, NiL, 0);
191 #endif
192 								}
193 								else	b = 0;
194 								break;
195 							}
196 							if (m)
197 								_Sftest |= b;
198 							else
199 								_Sftest &= ~b;
200 						} while ((v = strchr(v, ',')) && ++v);
201 					break;
202 				case 'W':		/* SF_WCWIDTH */
203 				case 'w':
204 					if(n)
205 						modes |= SF_WCWIDTH;
206 					else
207 						modes &= ~SF_WCWIDTH;
208 					break;
209 				}
210 			}
211 		}
212 	}
213 
214 	return modes;
215 }
216 
217 #endif
218 
219 #if __STD_C
sfsetbuf(Sfio_t * f,Void_t * buf,size_t size)220 Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)
221 #else
222 Void_t* sfsetbuf(f,buf,size)
223 Sfio_t*	f;	/* stream to be buffered */
224 Void_t*	buf;	/* new buffer */
225 size_t	size;	/* buffer size, -1 for default size */
226 #endif
227 {
228 	int		sf_malloc, oflags, init, okmmap, local;
229 	ssize_t		bufsize, blksz;
230 	Sfdisc_t*	disc;
231 	sfstat_t	st;
232 	uchar*		obuf = NIL(uchar*);
233 	ssize_t		osize = 0;
234 	SFMTXDECL(f);
235 
236 	SFONCE();
237 
238 	SFMTXENTER(f,NIL(Void_t*));
239 
240 	GETLOCAL(f,local);
241 
242 	if(size == 0 && buf)
243 	{	/* special case to get buffer info */
244 		_Sfi = f->val = (f->bits&SF_MMAP) ? (f->endb-f->data) : f->size;
245 		SFMTXRETURN(f, (Void_t*)f->data);
246 	}
247 
248 	/* cleanup actions already done, don't allow write buffering any more */
249 	if(_Sfexiting && !(f->flags&SF_STRING) && (f->mode&SF_WRITE))
250 	{	buf = NIL(Void_t*);
251 		size = 0;
252 	}
253 
254 	if((init = f->mode&SF_INIT) )
255 	{	if(!f->pool && _sfsetpool(f) < 0)
256 			SFMTXRETURN(f, NIL(Void_t*));
257 	}
258 	else if((f->mode&SF_RDWR) != SFMODE(f,local) && _sfmode(f,0,local) < 0)
259 		SFMTXRETURN(f, NIL(Void_t*));
260 
261 	if(init)
262 		f->mode = (f->mode&SF_RDWR)|SF_LOCK;
263 	else
264 	{	int	rv;
265 
266 		/* make sure there is no hidden read data */
267 		if(f->proc && (f->flags&SF_READ) && (f->mode&SF_WRITE) &&
268 		   _sfmode(f,SF_READ,local) < 0)
269 			SFMTXRETURN(f, NIL(Void_t*));
270 
271 		/* synchronize first */
272 		SFLOCK(f,local); rv = SFSYNC(f); SFOPEN(f,local);
273 		if(rv < 0)
274 			SFMTXRETURN(f, NIL(Void_t*));
275 
276 		/* turn off the SF_SYNCED bit because buffer is changing */
277 		f->mode &= ~SF_SYNCED;
278 	}
279 
280 	SFLOCK(f,local);
281 
282 	if((Sfio_t*)buf != f)
283 		blksz = -1;
284 	else /* setting alignment size only */
285 	{	blksz = (ssize_t)size;
286 
287 		if(!init) /* stream already initialized */
288 		{	obuf = f->data;
289 			osize = f->size;
290 			goto done;
291 		}
292 		else /* initialize stream as if in the default case */
293 		{	buf = NIL(Void_t*);
294 			size = (size_t)SF_UNBOUND;
295 		}
296 	}
297 
298 	bufsize = 0;
299 	oflags = f->flags;
300 
301 	/* see if memory mapping is possible (see sfwrite for SF_BOTH) */
302 	okmmap = (buf || (f->flags&SF_STRING) || (f->flags&SF_RDWR) == SF_RDWR) ? 0 : 1;
303 
304 	/* save old buffer info */
305 	if(f->bits&SF_MMAP)
306 	{	if(f->data)
307 		{	if(f->getr && (f->mode&SF_GETR) && f->next)
308 				f->next[-1] = f->getr;
309 			SFMUNMAP(f,f->data,f->endb-f->data);
310 			f->data = NIL(uchar*);
311 		}
312 	} else
313 	if(f->data == f->tiny)
314 	{	f->data = NIL(uchar*);
315 		f->size = 0;
316 	}
317 	obuf  = f->data;
318 	osize = f->size;
319 
320 	f->flags &= ~SF_MALLOC;
321 	f->bits  &= ~SF_MMAP;
322 	f->mode &= ~SF_GETR;
323 	f->getr = 0;
324 
325 	/* pure read/string streams must have a valid string */
326 	if((f->flags&(SF_RDWR|SF_STRING)) == SF_RDSTR &&
327 	   (size == (size_t)SF_UNBOUND || !buf))
328 		size = 0;
329 
330 	/* set disc to the first discipline with a seekf */
331 	for(disc = f->disc; disc; disc = disc->disc)
332 		if(disc->seekf)
333 			break;
334 
335 	if((init || local) && !(f->flags&SF_STRING))
336 	{	/* ASSERT(f->file >= 0) */
337 		st.st_mode = 0;
338 
339 		/* if has discipline, set size by discipline if possible */
340 		if(!_sys_stat || disc)
341 		{	if((f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,disc)) < 0)
342 				goto unseekable;
343 			else
344 			{	Sfoff_t	e;
345 				if((e = SFSK(f,(Sfoff_t)0,SEEK_END,disc)) >= 0)
346 					f->extent = e > f->here ? e : f->here;
347 				(void)SFSK(f,f->here,SEEK_SET,disc);
348 				goto setbuf;
349 			}
350 		}
351 
352 		/* get file descriptor status */
353 		if(sysfstatf((int)f->file,&st) < 0)
354 			f->here = -1;
355 		else
356 		{
357 #if _sys_stat && _stat_blksize	/* preferred io block size */
358 			f->blksz = (size_t)st.st_blksize;
359 #endif
360 			bufsize = 64 * 1024;
361 			if(S_ISDIR(st.st_mode) || (Sfoff_t)st.st_size < (Sfoff_t)SF_GRAIN)
362 				okmmap = 0;
363 			if(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
364 				f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,f->disc);
365 			else	f->here = -1;
366 
367 #if O_TEXT /* no memory mapping with O_TEXT because read()/write() alter data stream */
368 			if(okmmap && f->here >= 0 &&
369 			   (sysfcntlf((int)f->file,F_GETFL,0) & O_TEXT) )
370 				okmmap = 0;
371 #endif
372 		}
373 
374 		/* set page size, this is also the desired default buffer size */
375 		if(_Sfpage <= 0)
376 		{
377 #if _lib_getpagesize
378 			if((_Sfpage = (size_t)getpagesize()) <= 0)
379 #endif
380 				_Sfpage = SF_PAGE;
381 		}
382 
383 #if SFSETLINEMODE
384 		if(init)
385 			f->flags |= sfsetlinemode();
386 #endif
387 
388 		if(f->here >= 0)
389 		{	f->extent = (Sfoff_t)st.st_size;
390 
391 			/* seekable std-devices are share-public by default */
392 			if(f == sfstdin || f == sfstdout || f == sfstderr)
393 				f->flags |= SF_SHARE|SF_PUBLIC;
394 		}
395 		else
396 		{
397 		unseekable:
398 			f->extent = -1;
399 			f->here = 0;
400 
401 			if(init)
402 			{	if(S_ISCHR(st.st_mode) )
403 				{	int oerrno = errno;
404 
405 					bufsize = SF_GRAIN;
406 
407 					/* set line mode for terminals */
408 					if(!(f->flags&(SF_LINE|SF_WCWIDTH)) && isatty(f->file))
409 						f->flags |= SF_LINE|SF_WCWIDTH;
410 #if _sys_stat
411 					else	/* special case /dev/null */
412 					{	reg int	dev, ino;
413 						static int null_checked, null_dev, null_ino;
414 						dev = (int)st.st_dev;
415 						ino = (int)st.st_ino;
416 						if(!null_checked)
417 						{	if(sysstatf(DEVNULL,&st) < 0)
418 								null_checked = -1;
419 							else
420 							{	null_checked = 1;
421 								null_dev = (int)st.st_dev;
422 								null_ino = (int)st.st_ino;
423 							}
424 						}
425 						if(null_checked >= 0 && dev == null_dev && ino == null_ino)
426 							SFSETNULL(f);
427 					}
428 #endif
429 					errno = oerrno;
430 				}
431 
432 				/* initialize side buffer for r+w unseekable streams */
433 				if(!f->proc && (f->bits&SF_BOTH) )
434 					(void)_sfpopen(f,-1,-1,1);
435 			}
436 		}
437 	}
438 
439 	if(okmmap && size && (f->mode&SF_READ) && f->extent >= 0 )
440 	{	/* see if we can try memory mapping */
441 		if(!disc)
442 			for(disc = f->disc; disc; disc = disc->disc)
443 				if(disc->readf)
444 					break;
445 		if(!disc)
446 		{	if(!(_Sftest & SF_TEST_read))
447 				f->bits |= SF_MMAP;
448 			if(size == (size_t)SF_UNBOUND)
449 			{	if(bufsize > _Sfpage)
450 					size = bufsize * SF_NMAP;
451 				else	size = _Sfpage * SF_NMAP;
452 				if(size > 256*1024)
453 					size = 256*1024;
454 			}
455 		}
456 	}
457 
458 	/* get buffer space */
459 setbuf:
460 	if(size == (size_t)SF_UNBOUND)
461 	{	/* define a default size suitable for block transfer */
462 		if(init && osize > 0)
463 			size = osize;
464 		else if(f == sfstderr && (f->mode&SF_WRITE))
465 			size = 0;
466 		else if(f->flags&SF_STRING )
467 			size = SF_GRAIN;
468 		else if((f->flags&SF_READ) && !(f->bits&SF_BOTH) &&
469 			f->extent > 0 && f->extent < (Sfoff_t)_Sfpage )
470 			size = (((size_t)f->extent + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN;
471 		else if((ssize_t)(size = _Sfpage) < bufsize)
472 			size = bufsize;
473 
474 		buf = NIL(Void_t*);
475 	}
476 
477 	sf_malloc = 0;
478 	if(size > 0 && !buf && !(f->bits&SF_MMAP))
479 	{	/* try to allocate a buffer */
480 		if(obuf && size == (size_t)osize && init)
481 		{	buf = (Void_t*)obuf;
482 			obuf = NIL(uchar*);
483 			sf_malloc = (oflags&SF_MALLOC);
484 		}
485 		if(!buf)
486 		{	/* do allocation */
487 			while(!buf && size > 0)
488 			{	if((buf = (Void_t*)malloc(size)) )
489 					break;
490 				else	size /= 2;
491 			}
492 			if(size > 0)
493 				sf_malloc = SF_MALLOC;
494 		}
495 	}
496 
497 	if(size == 0 && !(f->flags&SF_STRING) && !(f->bits&SF_MMAP) && (f->mode&SF_READ))
498 	{	/* use the internal buffer */
499 		size = sizeof(f->tiny);
500 		buf = (Void_t*)f->tiny;
501 	}
502 
503 	/* set up new buffer */
504 	f->size = size;
505 	f->next = f->data = f->endr = f->endw = (uchar*)buf;
506 	f->endb = (f->mode&SF_READ) ? f->data : f->data+size;
507 	if(f->flags&SF_STRING)
508 	{	/* these fields are used to test actual size - see sfseek() */
509 		f->extent = (!sf_malloc &&
510 			     ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ) ? size : 0;
511 		f->here = 0;
512 
513 		/* read+string stream should have all data available */
514 		if((f->mode&SF_READ) && !sf_malloc)
515 			f->endb = f->data+size;
516 	}
517 
518 	f->flags = (f->flags & ~SF_MALLOC)|sf_malloc;
519 
520 	if(obuf && obuf != f->data && osize > 0 && (oflags&SF_MALLOC))
521 	{	free((Void_t*)obuf);
522 		obuf = NIL(uchar*);
523 	}
524 
525 done:
526 	_Sfi = f->val = obuf ? osize : 0;
527 
528 	/* blksz is used for aligning disk block boundary while reading data to
529 	** optimize data transfer from disk (eg, via direct I/O). blksz can be
530 	** at most f->size/2 so that data movement in buffer can be optimized.
531 	** blksz should also be a power-of-2 for optimal disk seeks.
532 	*/
533 	if(blksz <= 0 || (blksz & (blksz-1)) != 0 )
534 		blksz = SF_GRAIN;
535 	while(blksz > f->size/2)
536 		blksz /= 2;
537 	f->blksz = blksz;
538 
539 	SFOPEN(f,local);
540 
541 	SFMTXRETURN(f, (Void_t*)obuf);
542 }
543