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 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 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