1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 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
setlinemode()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
sfsetbuf(Sfio_t * f,Void_t * buf,size_t size)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