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