1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2012 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 #include "sfhdr.h"
23 static char* Version = "\n@(#)$Id: sfio (AT&T Labs - Research) 2009-09-15 $\0\n";
24
25 /* Functions to set a given stream to some desired mode
26 **
27 ** Written by Kiem-Phong Vo.
28 **
29 ** Modifications:
30 ** 06/27/1990 (first version)
31 ** 01/06/1991
32 ** 07/08/1991
33 ** 06/18/1992
34 ** 02/02/1993
35 ** 05/25/1993
36 ** 02/07/1994
37 ** 05/21/1996
38 ** 08/01/1997
39 ** 08/01/1998 (extended formatting)
40 ** 09/09/1999 (thread-safe)
41 ** 02/01/2001 (adaptive buffering)
42 ** 05/31/2002 (multi-byte handling in sfvprintf/vscanf)
43 ** 09/06/2002 (SF_IOINTR flag)
44 ** 11/15/2002 (%#c for sfvprintf)
45 ** 05/31/2003 (sfsetbuf(f,f,align_size) to set alignment for data)
46 ** (%I1d is fixed to handle "signed char" correctly)
47 ** 01/01/2004 Porting issues to various platforms resolved.
48 ** 06/01/2008 Allowing notify() at entering/exiting thread-safe routines.
49 ** 09/15/2008 Add sfwalk().
50 */
51
52 /* the below is for protecting the application from SIGPIPE */
53 #if _PACKAGE_ast
54 #include <sig.h>
55 #include <wait.h>
56 #define Sfsignal_f Sig_handler_t
57 #else
58 #include <signal.h>
59 typedef void(* Sfsignal_f)_ARG_((int));
60 #endif
61 static int _Sfsigp = 0; /* # of streams needing SIGPIPE protection */
62
63 /* done at exiting time */
64 #if __STD_C
_sfcleanup(void)65 static void _sfcleanup(void)
66 #else
67 static void _sfcleanup()
68 #endif
69 {
70 reg Sfpool_t* p;
71 reg Sfio_t* f;
72 reg int n;
73 reg int pool;
74
75 f = (Sfio_t*)Version; /* shut compiler warning */
76
77 /* set this so that no more buffering is allowed for write streams */
78 _Sfexiting = 1001;
79
80 sfsync(NIL(Sfio_t*));
81
82 for(p = &_Sfpool; p; p = p->next)
83 { for(n = 0; n < p->n_sf; ++n)
84 { if(!(f = p->sf[n]) || SFFROZEN(f) )
85 continue;
86
87 SFLOCK(f,0);
88 SFMTXLOCK(f);
89
90 /* let application know that we are leaving */
91 (void)SFRAISE(f, SF_ATEXIT, NIL(Void_t*));
92
93 if(f->flags&SF_STRING)
94 continue;
95
96 /* from now on, write streams are unbuffered */
97 pool = f->mode&SF_POOL;
98 f->mode &= ~SF_POOL;
99 if((f->flags&SF_WRITE) && !(f->mode&SF_WRITE))
100 (void)_sfmode(f,SF_WRITE,1);
101 if(f->data &&
102 ((f->bits&SF_MMAP) ||
103 ((f->mode&SF_WRITE) && f->next == f->data) ) )
104 (void)SFSETBUF(f,NIL(Void_t*),0);
105 f->mode |= pool;
106
107 SFMTXUNLOCK(f);
108 SFOPEN(f,0);
109 }
110 }
111 }
112
113 /* put into discrete pool */
114 #if __STD_C
_sfsetpool(Sfio_t * f)115 int _sfsetpool(Sfio_t* f)
116 #else
117 int _sfsetpool(f)
118 Sfio_t* f;
119 #endif
120 {
121 reg Sfpool_t* p;
122 reg Sfio_t** array;
123 reg int n, rv;
124
125 if(!_Sfcleanup)
126 { _Sfcleanup = _sfcleanup;
127 (void)atexit(_sfcleanup);
128 }
129
130 if(!(p = f->pool) )
131 p = f->pool = &_Sfpool;
132
133 POOLMTXENTER(p);
134
135 rv = -1;
136
137 if(p->n_sf >= p->s_sf)
138 { if(p->s_sf == 0) /* initialize pool array */
139 { p->s_sf = sizeof(p->array)/sizeof(p->array[0]);
140 p->sf = p->array;
141 }
142 else /* allocate a larger array */
143 { n = (p->sf != p->array ? p->s_sf : (p->s_sf/4 + 1)*4) + 4;
144 if(!(array = (Sfio_t**)malloc(n*sizeof(Sfio_t*))) )
145 goto done;
146
147 /* move old array to new one */
148 memcpy((Void_t*)array,(Void_t*)p->sf,p->n_sf*sizeof(Sfio_t*));
149 if(p->sf != p->array)
150 free((Void_t*)p->sf);
151
152 p->sf = array;
153 p->s_sf = n;
154 }
155 }
156
157 /* always add at end of array because if this was done during some sort
158 of walk thru all streams, we'll want the new stream to be seen.
159 */
160 p->sf[p->n_sf++] = f;
161 rv = 0;
162
163 done:
164 POOLMTXRETURN(p, rv);
165 }
166
167 /* create an auxiliary buffer for sfgetr/sfreserve/sfputr */
168 #if __STD_C
_sfrsrv(reg Sfio_t * f,reg ssize_t size)169 Sfrsrv_t* _sfrsrv(reg Sfio_t* f, reg ssize_t size)
170 #else
171 Sfrsrv_t* _sfrsrv(f,size)
172 reg Sfio_t* f;
173 reg ssize_t size;
174 #endif
175 {
176 Sfrsrv_t *rsrv, *rs;
177
178 /* make buffer if nothing yet */
179 size = ((size + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN;
180 if(!(rsrv = f->rsrv) || size > rsrv->size)
181 { if(!(rs = (Sfrsrv_t*)malloc(size+sizeof(Sfrsrv_t))))
182 size = -1;
183 else
184 { if(rsrv)
185 { if(rsrv->slen > 0)
186 memcpy(rs,rsrv,sizeof(Sfrsrv_t)+rsrv->slen);
187 free(rsrv);
188 }
189 f->rsrv = rsrv = rs;
190 rsrv->size = size;
191 rsrv->slen = 0;
192 }
193 }
194
195 if(rsrv && size > 0)
196 rsrv->slen = 0;
197
198 return size >= 0 ? rsrv : NIL(Sfrsrv_t*);
199 }
200
201 #ifdef SIGPIPE
202 #if __STD_C
ignoresig(int sig)203 static void ignoresig(int sig)
204 #else
205 static void ignoresig(sig)
206 int sig;
207 #endif
208 {
209 signal(sig, ignoresig);
210 }
211 #endif
212
213 #if __STD_C
_sfpopen(reg Sfio_t * f,int fd,int pid,int stdio)214 int _sfpopen(reg Sfio_t* f, int fd, int pid, int stdio)
215 #else
216 int _sfpopen(f, fd, pid, stdio)
217 reg Sfio_t* f;
218 int fd;
219 int pid;
220 int stdio; /* stdio popen() does not reset SIGPIPE handler */
221 #endif
222 {
223 reg Sfproc_t* p;
224
225 if(f->proc)
226 return 0;
227
228 if(!(p = f->proc = (Sfproc_t*)malloc(sizeof(Sfproc_t))) )
229 return -1;
230
231 p->pid = pid;
232 p->size = p->ndata = 0;
233 p->rdata = NIL(uchar*);
234 p->file = fd;
235 p->sigp = (!stdio && pid >= 0 && (f->flags&SF_WRITE)) ? 1 : 0;
236
237 #ifdef SIGPIPE /* protect from broken pipe signal */
238 if(p->sigp)
239 { Sfsignal_f handler;
240
241 (void)vtmtxlock(_Sfmutex);
242 if((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL &&
243 handler != ignoresig)
244 signal(SIGPIPE, handler); /* honor user handler */
245 _Sfsigp += 1;
246 (void)vtmtxunlock(_Sfmutex);
247 }
248 #endif
249
250 return 0;
251 }
252
253 #if __STD_C
_sfpclose(reg Sfio_t * f)254 int _sfpclose(reg Sfio_t* f)
255 #else
256 int _sfpclose(f)
257 reg Sfio_t* f; /* stream to close */
258 #endif
259 {
260 Sfproc_t* p;
261 int pid, status;
262
263 if(!(p = f->proc))
264 return -1;
265 f->proc = NIL(Sfproc_t*);
266
267 if(p->rdata)
268 free(p->rdata);
269
270 if(p->pid < 0)
271 status = 0;
272 else
273 { /* close the associated stream */
274 if(p->file >= 0)
275 CLOSE(p->file);
276
277 /* wait for process termination */
278 #if _PACKAGE_ast
279 sigcritical(SIG_REG_EXEC|SIG_REG_PROC);
280 #endif
281 status = -1;
282 while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR)
283 ;
284 #if _PACKAGE_ast
285 status = status == -1 ?
286 EXIT_QUIT :
287 WIFSIGNALED(status) ?
288 EXIT_TERM(WTERMSIG(status)) :
289 EXIT_CODE(WEXITSTATUS(status));
290 sigcritical(0);
291 #endif
292
293 #ifdef SIGPIPE
294 (void)vtmtxlock(_Sfmutex);
295 if(p->sigp && (_Sfsigp -= 1) <= 0)
296 { Sfsignal_f handler;
297 if((handler = signal(SIGPIPE,SIG_DFL)) != SIG_DFL &&
298 handler != ignoresig)
299 signal(SIGPIPE,handler); /* honor user handler */
300 _Sfsigp = 0;
301 }
302 (void)vtmtxunlock(_Sfmutex);
303 #endif
304 }
305
306 free(p);
307 return status;
308 }
309
310 #if __STD_C
_sfpmode(Sfio_t * f,int type)311 static int _sfpmode(Sfio_t* f, int type)
312 #else
313 static int _sfpmode(f,type)
314 Sfio_t* f;
315 int type;
316 #endif
317 {
318 Sfproc_t* p;
319
320 if(!(p = f->proc) )
321 return -1;
322
323 if(type == SF_WRITE)
324 { /* save unread data */
325 p->ndata = f->endb-f->next;
326 if(p->ndata > p->size)
327 { if(p->rdata)
328 free((char*)p->rdata);
329 if((p->rdata = (uchar*)malloc(p->ndata)) )
330 p->size = p->ndata;
331 else
332 { p->size = 0;
333 return -1;
334 }
335 }
336 if(p->ndata > 0)
337 memcpy((Void_t*)p->rdata,(Void_t*)f->next,p->ndata);
338 f->endb = f->data;
339 }
340 else
341 { /* restore read data */
342 if(p->ndata > f->size) /* may lose data!!! */
343 p->ndata = f->size;
344 if(p->ndata > 0)
345 { memcpy((Void_t*)f->data,(Void_t*)p->rdata,p->ndata);
346 f->endb = f->data+p->ndata;
347 p->ndata = 0;
348 }
349 }
350
351 /* switch file descriptor */
352 if(p->pid >= 0)
353 { type = f->file;
354 f->file = p->file;
355 p->file = type;
356 }
357
358 return 0;
359 }
360
361 #if __STD_C
_sfmode(reg Sfio_t * f,reg int wanted,reg int local)362 int _sfmode(reg Sfio_t* f, reg int wanted, reg int local)
363 #else
364 int _sfmode(f, wanted, local)
365 reg Sfio_t* f; /* change r/w mode and sync file pointer for this stream */
366 reg int wanted; /* desired mode */
367 reg int local; /* a local call */
368 #endif
369 {
370 reg int n;
371 Sfoff_t addr;
372 reg int rv = 0;
373
374 SFONCE(); /* initialize mutexes */
375
376 if(wanted&SF_SYNCED) /* for (SF_SYNCED|SF_READ) stream, just junk data */
377 { wanted &= ~SF_SYNCED;
378 if((f->mode&(SF_SYNCED|SF_READ)) == (SF_SYNCED|SF_READ) )
379 { f->next = f->endb = f->endr = f->data;
380 f->mode &= ~SF_SYNCED;
381 }
382 }
383
384 if((!local && SFFROZEN(f)) || (!(f->flags&SF_STRING) && f->file < 0))
385 { if(local || !f->disc || !f->disc->exceptf)
386 { local = 1;
387 goto err_notify;
388 }
389
390 for(;;)
391 { if((rv = (*f->disc->exceptf)(f,SF_LOCKED,0,f->disc)) < 0)
392 return rv;
393 if((!local && SFFROZEN(f)) ||
394 (!(f->flags&SF_STRING) && f->file < 0) )
395 { if(rv == 0)
396 { local = 1;
397 goto err_notify;
398 }
399 else continue;
400 }
401 else break;
402 }
403 }
404
405 if(f->mode&SF_GETR)
406 { f->mode &= ~SF_GETR;
407 #ifdef MAP_TYPE
408 if((f->bits&SF_MMAP) && (f->tiny[0] += 1) >= (4*SF_NMAP) )
409 { /* turn off mmap to avoid page faulting */
410 sfsetbuf(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
411 f->tiny[0] = 0;
412 }
413 else
414 #endif
415 if(f->getr)
416 { f->next[-1] = f->getr;
417 f->getr = 0;
418 }
419 }
420
421 if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */
422 (*_Sfstdsync)(f);
423
424 if(f->disc == _Sfudisc && wanted == SF_WRITE &&
425 sfclose((*_Sfstack)(f,NIL(Sfio_t*))) < 0 )
426 { local = 1;
427 goto err_notify;
428 }
429
430 if(f->mode&SF_POOL)
431 { /* move to head of pool */
432 if(f == f->pool->sf[0] || (*_Sfpmove)(f,0) < 0 )
433 { local = 1;
434 goto err_notify;
435 }
436 f->mode &= ~SF_POOL;
437 }
438
439 SFLOCK(f,local);
440
441 /* buffer initialization */
442 wanted &= SF_RDWR;
443 if(f->mode&SF_INIT)
444 {
445 if(!f->pool && _sfsetpool(f) < 0)
446 { rv = -1;
447 goto done;
448 }
449
450 if(wanted == 0)
451 goto done;
452
453 if(wanted != (int)(f->mode&SF_RDWR) && !(f->flags&wanted) )
454 goto err_notify;
455
456 if((f->flags&SF_STRING) && f->size >= 0 && f->data)
457 { f->mode &= ~SF_INIT;
458 f->extent = ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ?
459 f->size : 0;
460 f->here = 0;
461 f->endb = f->data + f->size;
462 f->next = f->endr = f->endw = f->data;
463 if(f->mode&SF_READ)
464 f->endr = f->endb;
465 else f->endw = f->endb;
466 }
467 else
468 { n = f->flags;
469 (void)SFSETBUF(f,f->data,f->size);
470 f->flags |= (n&SF_MALLOC);
471 }
472 }
473
474 if(wanted == (int)SFMODE(f,1))
475 goto done;
476
477 switch(SFMODE(f,1))
478 {
479 case SF_WRITE: /* switching to SF_READ */
480 if(wanted == 0 || wanted == SF_WRITE)
481 break;
482 if(!(f->flags&SF_READ) )
483 goto err_notify;
484 else if(f->flags&SF_STRING)
485 { SFSTRSIZE(f);
486 f->endb = f->data+f->extent;
487 f->mode = SF_READ;
488 break;
489 }
490
491 /* reset buffer */
492 if(f->next > f->data && SFFLSBUF(f,-1) < 0)
493 goto err_notify;
494
495 if(f->size == 0)
496 { /* unbuffered */
497 f->data = f->tiny;
498 f->size = sizeof(f->tiny);
499 }
500 f->next = f->endr = f->endw = f->endb = f->data;
501 f->mode = SF_READ|SF_LOCK;
502
503 /* restore saved read data for coprocess */
504 if(f->proc && _sfpmode(f,wanted) < 0)
505 goto err_notify;
506
507 break;
508
509 case (SF_READ|SF_SYNCED): /* a previously sync-ed read stream */
510 if(wanted != SF_WRITE)
511 { /* just reset the pointers */
512 f->mode = SF_READ|SF_LOCK;
513
514 /* see if must go with new physical location */
515 if((f->flags&(SF_SHARE|SF_PUBLIC)) == (SF_SHARE|SF_PUBLIC) &&
516 (addr = SFSK(f,0,SEEK_CUR,f->disc)) != f->here)
517 {
518 #ifdef MAP_TYPE
519 if((f->bits&SF_MMAP) && f->data)
520 { SFMUNMAP(f,f->data,f->endb-f->data);
521 f->data = NIL(uchar*);
522 }
523 #endif
524 f->endb = f->endr = f->endw = f->next = f->data;
525 f->here = addr;
526 }
527 else
528 { addr = f->here + (f->endb - f->next);
529 if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
530 goto err_notify;
531 f->here = addr;
532 }
533
534 break;
535 }
536 /* fall thru */
537
538 case SF_READ: /* switching to SF_WRITE */
539 if(wanted != SF_WRITE)
540 break;
541 else if(!(f->flags&SF_WRITE))
542 goto err_notify;
543 else if(f->flags&SF_STRING)
544 { f->endb = f->data+f->size;
545 f->mode = SF_WRITE|SF_LOCK;
546 break;
547 }
548
549 /* save unread data before switching mode */
550 if(f->proc && _sfpmode(f,wanted) < 0)
551 goto err_notify;
552
553 /* reset buffer and seek pointer */
554 if(!(f->mode&SF_SYNCED) )
555 { n = f->endb - f->next;
556 if(f->extent >= 0 && (n > 0 || (f->data && (f->bits&SF_MMAP))) )
557 { /* reset file pointer */
558 addr = f->here - n;
559 if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
560 goto err_notify;
561 f->here = addr;
562 }
563 }
564
565 f->mode = SF_WRITE|SF_LOCK;
566 #ifdef MAP_TYPE
567 if(f->bits&SF_MMAP)
568 { if(f->data)
569 SFMUNMAP(f,f->data,f->endb-f->data);
570 (void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
571 }
572 #endif
573 if(f->data == f->tiny)
574 { f->endb = f->data = f->next = NIL(uchar*);
575 f->size = 0;
576 }
577 else f->endb = (f->next = f->data) + f->size;
578
579 break;
580
581 default: /* unknown case */
582 err_notify:
583 if((wanted &= SF_RDWR) == 0 && (wanted = f->flags&SF_RDWR) == SF_RDWR)
584 wanted = SF_READ;
585
586 /* set errno for operations that access wrong stream type */
587 if(wanted != (f->mode&SF_RDWR) && f->file >= 0)
588 errno = EBADF;
589
590 if(_Sfnotify) /* notify application of the error */
591 (*_Sfnotify)(f, wanted, (void*)((long)f->file));
592
593 rv = -1;
594 break;
595 }
596
597 done:
598 SFOPEN(f,local);
599 return rv;
600 }
601