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