1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
23 static char* Version
= "\n@(#)$Id: sfio (AT&T Labs - Research) 2009-09-15 $\0\n";
25 /* Functions to set a given stream to some desired mode
27 ** Written by Kiem-Phong Vo.
30 ** 06/27/1990 (first version)
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().
52 /* the below is for protecting the application from SIGPIPE */
56 #define Sfsignal_f Sig_handler_t
59 typedef void(* Sfsignal_f
)_ARG_((int));
61 static int _Sfsigp
= 0; /* # of streams needing SIGPIPE protection */
63 /* done at exiting time */
65 static void _sfcleanup(void)
67 static void _sfcleanup()
75 f
= (Sfio_t
*)Version
; /* shut compiler warning */
77 /* set this so that no more buffering is allowed for write streams */
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
) )
90 /* let application know that we are leaving */
91 (void)SFRAISE(f
, SF_ATEXIT
, NIL(Void_t
*));
93 if(f
->flags
&SF_STRING
)
96 /* from now on, write streams are unbuffered */
97 pool
= f
->mode
&SF_POOL
;
99 if((f
->flags
&SF_WRITE
) && !(f
->mode
&SF_WRITE
))
100 (void)_sfmode(f
,SF_WRITE
,1);
101 if(((f
->bits
&SF_MMAP
) && f
->data
) ||
102 ((f
->mode
&SF_WRITE
) && f
->next
== f
->data
) )
103 (void)SFSETBUF(f
,NIL(Void_t
*),0);
112 /* put into discrete pool */
114 int _sfsetpool(Sfio_t
* f
)
125 { _Sfcleanup
= _sfcleanup
;
126 (void)atexit(_sfcleanup
);
130 p
= f
->pool
= &_Sfpool
;
136 if(p
->n_sf
>= p
->s_sf
)
137 { if(p
->s_sf
== 0) /* initialize pool array */
138 { p
->s_sf
= sizeof(p
->array
)/sizeof(p
->array
[0]);
141 else /* allocate a larger array */
142 { n
= (p
->sf
!= p
->array
? p
->s_sf
: (p
->s_sf
/4 + 1)*4) + 4;
143 if(!(array
= (Sfio_t
**)malloc(n
*sizeof(Sfio_t
*))) )
146 /* move old array to new one */
147 memcpy((Void_t
*)array
,(Void_t
*)p
->sf
,p
->n_sf
*sizeof(Sfio_t
*));
148 if(p
->sf
!= p
->array
)
149 free((Void_t
*)p
->sf
);
156 /* always add at end of array because if this was done during some sort
157 of walk thru all streams, we'll want the new stream to be seen.
159 p
->sf
[p
->n_sf
++] = f
;
163 POOLMTXRETURN(p
, rv
);
166 /* create an auxiliary buffer for sfgetr/sfreserve/sfputr */
168 Sfrsrv_t
* _sfrsrv(reg Sfio_t
* f
, reg ssize_t size
)
170 Sfrsrv_t
* _sfrsrv(f
,size
)
177 /* make buffer if nothing yet */
178 size
= ((size
+ SF_GRAIN
-1)/SF_GRAIN
)*SF_GRAIN
;
179 if(!(rsrv
= f
->rsrv
) || size
> rsrv
->size
)
180 { if(!(rs
= (Sfrsrv_t
*)malloc(size
+sizeof(Sfrsrv_t
))))
185 memcpy(rs
,rsrv
,sizeof(Sfrsrv_t
)+rsrv
->slen
);
197 return size
>= 0 ? rsrv
: NIL(Sfrsrv_t
*);
202 static void ignoresig(int sig
)
204 static void ignoresig(sig
)
208 signal(sig
, ignoresig
);
213 int _sfpopen(reg Sfio_t
* f
, int fd
, int pid
, int stdio
)
215 int _sfpopen(f
, fd
, pid
, stdio
)
219 int stdio
; /* stdio popen() does not reset SIGPIPE handler */
227 if(!(p
= f
->proc
= (Sfproc_t
*)malloc(sizeof(Sfproc_t
))) )
231 p
->size
= p
->ndata
= 0;
232 p
->rdata
= NIL(uchar
*);
234 p
->sigp
= (!stdio
&& pid
>= 0 && (f
->flags
&SF_WRITE
)) ? 1 : 0;
236 #ifdef SIGPIPE /* protect from broken pipe signal */
238 { Sfsignal_f handler
;
240 (void)vtmtxlock(_Sfmutex
);
241 if((handler
= signal(SIGPIPE
, ignoresig
)) != SIG_DFL
&&
242 handler
!= ignoresig
)
243 signal(SIGPIPE
, handler
); /* honor user handler */
245 (void)vtmtxunlock(_Sfmutex
);
253 int _sfpclose(reg Sfio_t
* f
)
256 reg Sfio_t
* f
; /* stream to close */
264 f
->proc
= NIL(Sfproc_t
*);
272 { /* close the associated stream */
276 /* wait for process termination */
278 sigcritical(SIG_REG_EXEC
|SIG_REG_PROC
);
280 while ((pid
= waitpid(p
->pid
,&status
,0)) == -1 && errno
== EINTR
)
289 (void)vtmtxlock(_Sfmutex
);
290 if(p
->sigp
&& (_Sfsigp
-= 1) <= 0)
291 { Sfsignal_f handler
;
292 if((handler
= signal(SIGPIPE
,SIG_DFL
)) != SIG_DFL
&&
293 handler
!= ignoresig
)
294 signal(SIGPIPE
,handler
); /* honor user handler */
297 (void)vtmtxunlock(_Sfmutex
);
306 static int _sfpmode(Sfio_t
* f
, int type
)
308 static int _sfpmode(f
,type
)
319 { /* save unread data */
320 p
->ndata
= f
->endb
-f
->next
;
321 if(p
->ndata
> p
->size
)
323 free((char*)p
->rdata
);
324 if((p
->rdata
= (uchar
*)malloc(p
->ndata
)) )
332 memcpy((Void_t
*)p
->rdata
,(Void_t
*)f
->next
,p
->ndata
);
336 { /* restore read data */
337 if(p
->ndata
> f
->size
) /* may lose data!!! */
340 { memcpy((Void_t
*)f
->data
,(Void_t
*)p
->rdata
,p
->ndata
);
341 f
->endb
= f
->data
+p
->ndata
;
346 /* switch file descriptor */
357 int _sfmode(reg Sfio_t
* f
, reg
int wanted
, reg
int local
)
359 int _sfmode(f
, wanted
, local
)
360 reg Sfio_t
* f
; /* change r/w mode and sync file pointer for this stream */
361 reg
int wanted
; /* desired mode */
362 reg
int local
; /* a local call */
369 SFONCE(); /* initialize mutexes */
371 if(wanted
&SF_SYNCED
) /* for (SF_SYNCED|SF_READ) stream, just junk data */
372 { wanted
&= ~SF_SYNCED
;
373 if((f
->mode
&(SF_SYNCED
|SF_READ
)) == (SF_SYNCED
|SF_READ
) )
374 { f
->next
= f
->endb
= f
->endr
= f
->data
;
375 f
->mode
&= ~SF_SYNCED
;
379 if((!local
&& SFFROZEN(f
)) || (!(f
->flags
&SF_STRING
) && f
->file
< 0))
380 { if(local
|| !f
->disc
|| !f
->disc
->exceptf
)
386 { if((rv
= (*f
->disc
->exceptf
)(f
,SF_LOCKED
,0,f
->disc
)) < 0)
388 if((!local
&& SFFROZEN(f
)) ||
389 (!(f
->flags
&SF_STRING
) && f
->file
< 0) )
401 { f
->mode
&= ~SF_GETR
;
403 if((f
->bits
&SF_MMAP
) && (f
->tiny
[0] += 1) >= (4*SF_NMAP
) )
404 { /* turn off mmap to avoid page faulting */
405 sfsetbuf(f
,(Void_t
*)f
->tiny
,(size_t)SF_UNBOUND
);
411 { f
->next
[-1] = f
->getr
;
416 if(f
->mode
&SF_STDIO
) /* synchronizing with stdio pointers */
419 if(f
->disc
== _Sfudisc
&& wanted
== SF_WRITE
&&
420 sfclose((*_Sfstack
)(f
,NIL(Sfio_t
*))) < 0 )
426 { /* move to head of pool */
427 if(f
== f
->pool
->sf
[0] || (*_Sfpmove
)(f
,0) < 0 )
436 /* buffer initialization */
440 if(!f
->pool
&& _sfsetpool(f
) < 0)
448 if(wanted
!= (int)(f
->mode
&SF_RDWR
) && !(f
->flags
&wanted
) )
451 if((f
->flags
&SF_STRING
) && f
->size
>= 0 && f
->data
)
452 { f
->mode
&= ~SF_INIT
;
453 f
->extent
= ((f
->flags
&SF_READ
) || (f
->bits
&SF_BOTH
)) ?
456 f
->endb
= f
->data
+ f
->size
;
457 f
->next
= f
->endr
= f
->endw
= f
->data
;
460 else f
->endw
= f
->endb
;
464 (void)SFSETBUF(f
,f
->data
,f
->size
);
465 f
->flags
|= (n
&SF_MALLOC
);
469 if(wanted
== (int)SFMODE(f
,1))
474 case SF_WRITE
: /* switching to SF_READ */
475 if(wanted
== 0 || wanted
== SF_WRITE
)
477 if(!(f
->flags
&SF_READ
) )
479 else if(f
->flags
&SF_STRING
)
481 f
->endb
= f
->data
+f
->extent
;
487 if(f
->next
> f
->data
&& SFFLSBUF(f
,-1) < 0)
493 f
->size
= sizeof(f
->tiny
);
495 f
->next
= f
->endr
= f
->endw
= f
->endb
= f
->data
;
496 f
->mode
= SF_READ
|SF_LOCK
;
498 /* restore saved read data for coprocess */
499 if(f
->proc
&& _sfpmode(f
,wanted
) < 0)
504 case (SF_READ
|SF_SYNCED
): /* a previously sync-ed read stream */
505 if(wanted
!= SF_WRITE
)
506 { /* just reset the pointers */
507 f
->mode
= SF_READ
|SF_LOCK
;
509 /* see if must go with new physical location */
510 if((f
->flags
&(SF_SHARE
|SF_PUBLIC
)) == (SF_SHARE
|SF_PUBLIC
) &&
511 (addr
= SFSK(f
,0,SEEK_CUR
,f
->disc
)) != f
->here
)
514 if((f
->bits
&SF_MMAP
) && f
->data
)
515 { SFMUNMAP(f
,f
->data
,f
->endb
-f
->data
);
516 f
->data
= NIL(uchar
*);
519 f
->endb
= f
->endr
= f
->endw
= f
->next
= f
->data
;
523 { addr
= f
->here
+ (f
->endb
- f
->next
);
524 if(SFSK(f
,addr
,SEEK_SET
,f
->disc
) < 0)
533 case SF_READ
: /* switching to SF_WRITE */
534 if(wanted
!= SF_WRITE
)
536 else if(!(f
->flags
&SF_WRITE
))
538 else if(f
->flags
&SF_STRING
)
539 { f
->endb
= f
->data
+f
->size
;
540 f
->mode
= SF_WRITE
|SF_LOCK
;
544 /* save unread data before switching mode */
545 if(f
->proc
&& _sfpmode(f
,wanted
) < 0)
548 /* reset buffer and seek pointer */
549 if(!(f
->mode
&SF_SYNCED
) )
550 { n
= f
->endb
- f
->next
;
551 if(f
->extent
>= 0 && (n
> 0 || (f
->data
&& (f
->bits
&SF_MMAP
))) )
552 { /* reset file pointer */
554 if(SFSK(f
,addr
,SEEK_SET
,f
->disc
) < 0)
560 f
->mode
= SF_WRITE
|SF_LOCK
;
564 SFMUNMAP(f
,f
->data
,f
->endb
-f
->data
);
565 (void)SFSETBUF(f
,(Void_t
*)f
->tiny
,(size_t)SF_UNBOUND
);
568 if(f
->data
== f
->tiny
)
569 { f
->endb
= f
->data
= f
->next
= NIL(uchar
*);
572 else f
->endb
= (f
->next
= f
->data
) + f
->size
;
576 default: /* unknown case */
578 if((wanted
&= SF_RDWR
) == 0 && (wanted
= f
->flags
&SF_RDWR
) == SF_RDWR
)
581 /* set errno for operations that access wrong stream type */
582 if(wanted
!= (f
->mode
&SF_RDWR
) && f
->file
>= 0)
585 if(_Sfnotify
) /* notify application of the error */
586 (*_Sfnotify
)(f
, wanted
, (void*)((long)f
->file
));