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*                  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#include	"sfhdr.h"
23static 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>
59typedef void(*		Sfsignal_f)_ARG_((int));
60#endif
61static int		_Sfsigp = 0; /* # of streams needing SIGPIPE protection */
62
63/* done at exiting time */
64#if __STD_C
65static void _sfcleanup(void)
66#else
67static 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
115int _sfsetpool(Sfio_t* f)
116#else
117int _sfsetpool(f)
118Sfio_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
163done:
164	POOLMTXRETURN(p, rv);
165}
166
167/* create an auxiliary buffer for sfgetr/sfreserve/sfputr */
168#if __STD_C
169Sfrsrv_t* _sfrsrv(reg Sfio_t* f, reg ssize_t size)
170#else
171Sfrsrv_t* _sfrsrv(f,size)
172reg Sfio_t*	f;
173reg 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
203static void ignoresig(int sig)
204#else
205static void ignoresig(sig)
206int sig;
207#endif
208{
209	signal(sig, ignoresig);
210}
211#endif
212
213#if __STD_C
214int _sfpopen(reg Sfio_t* f, int fd, int pid, int stdio)
215#else
216int _sfpopen(f, fd, pid, stdio)
217reg Sfio_t*	f;
218int		fd;
219int		pid;
220int		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
254int _sfpclose(reg Sfio_t* f)
255#else
256int _sfpclose(f)
257reg 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		while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR)
282			;
283		if(pid == -1)
284			status = -1;
285#if _PACKAGE_ast
286		sigcritical(0);
287#endif
288
289#ifdef SIGPIPE
290		(void)vtmtxlock(_Sfmutex);
291		if(p->sigp && (_Sfsigp -= 1) <= 0)
292		{	Sfsignal_f	handler;
293			if((handler = signal(SIGPIPE,SIG_DFL)) != SIG_DFL &&
294			   handler != ignoresig)
295				signal(SIGPIPE,handler); /* honor user handler */
296			_Sfsigp = 0;
297		}
298		(void)vtmtxunlock(_Sfmutex);
299#endif
300	}
301
302	free(p);
303	return status;
304}
305
306#if __STD_C
307static int _sfpmode(Sfio_t* f, int type)
308#else
309static int _sfpmode(f,type)
310Sfio_t*	f;
311int	type;
312#endif
313{
314	Sfproc_t*	p;
315
316	if(!(p = f->proc) )
317		return -1;
318
319	if(type == SF_WRITE)
320	{	/* save unread data */
321		p->ndata = f->endb-f->next;
322		if(p->ndata > p->size)
323		{	if(p->rdata)
324				free((char*)p->rdata);
325			if((p->rdata = (uchar*)malloc(p->ndata)) )
326				p->size = p->ndata;
327			else
328			{	p->size = 0;
329				return -1;
330			}
331		}
332		if(p->ndata > 0)
333			memcpy((Void_t*)p->rdata,(Void_t*)f->next,p->ndata);
334		f->endb = f->data;
335	}
336	else
337	{	/* restore read data */
338		if(p->ndata > f->size)	/* may lose data!!! */
339			p->ndata = f->size;
340		if(p->ndata > 0)
341		{	memcpy((Void_t*)f->data,(Void_t*)p->rdata,p->ndata);
342			f->endb = f->data+p->ndata;
343			p->ndata = 0;
344		}
345	}
346
347	/* switch file descriptor */
348	if(p->pid >= 0)
349	{	type = f->file;
350		f->file = p->file;
351		p->file = type;
352	}
353
354	return 0;
355}
356
357#if __STD_C
358int _sfmode(reg Sfio_t* f, reg int wanted, reg int local)
359#else
360int _sfmode(f, wanted, local)
361reg Sfio_t*	f;	/* change r/w mode and sync file pointer for this stream */
362reg int		wanted;	/* desired mode */
363reg int		local;	/* a local call */
364#endif
365{
366	reg int	n;
367	Sfoff_t	addr;
368	reg int	rv = 0;
369
370	SFONCE();	/* initialize mutexes */
371
372	if(wanted&SF_SYNCED) /* for (SF_SYNCED|SF_READ) stream, just junk data */
373	{	wanted &= ~SF_SYNCED;
374		if((f->mode&(SF_SYNCED|SF_READ)) == (SF_SYNCED|SF_READ) )
375		{	f->next = f->endb = f->endr = f->data;
376			f->mode &= ~SF_SYNCED;
377		}
378	}
379
380	if((!local && SFFROZEN(f)) || (!(f->flags&SF_STRING) && f->file < 0))
381	{	if(local || !f->disc || !f->disc->exceptf)
382		{	local = 1;
383			goto err_notify;
384		}
385
386		for(;;)
387		{	if((rv = (*f->disc->exceptf)(f,SF_LOCKED,0,f->disc)) < 0)
388				return rv;
389			if((!local && SFFROZEN(f)) ||
390			   (!(f->flags&SF_STRING) && f->file < 0) )
391			{	if(rv == 0)
392				{	local = 1;
393					goto err_notify;
394				}
395				else	continue;
396			}
397			else	break;
398		}
399	}
400
401	if(f->mode&SF_GETR)
402	{	f->mode &= ~SF_GETR;
403#ifdef MAP_TYPE
404		if((f->bits&SF_MMAP) && (f->tiny[0] += 1) >= (4*SF_NMAP) )
405		{	/* turn off mmap to avoid page faulting */
406			sfsetbuf(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
407			f->tiny[0] = 0;
408		}
409		else
410#endif
411		if(f->getr)
412		{	f->next[-1] = f->getr;
413			f->getr = 0;
414		}
415	}
416
417	if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */
418		(*_Sfstdsync)(f);
419
420	if(f->disc == _Sfudisc && wanted == SF_WRITE &&
421	   sfclose((*_Sfstack)(f,NIL(Sfio_t*))) < 0 )
422	{	local = 1;
423		goto err_notify;
424	}
425
426	if(f->mode&SF_POOL)
427	{	/* move to head of pool */
428		if(f == f->pool->sf[0] || (*_Sfpmove)(f,0) < 0 )
429		{	local = 1;
430			goto err_notify;
431		}
432		f->mode &= ~SF_POOL;
433	}
434
435	SFLOCK(f,local);
436
437	/* buffer initialization */
438	wanted &= SF_RDWR;
439	if(f->mode&SF_INIT)
440	{
441		if(!f->pool && _sfsetpool(f) < 0)
442		{	rv = -1;
443			goto done;
444		}
445
446		if(wanted == 0)
447			goto done;
448
449		if(wanted != (int)(f->mode&SF_RDWR) && !(f->flags&wanted) )
450			goto err_notify;
451
452		if((f->flags&SF_STRING) && f->size >= 0 && f->data)
453		{	f->mode &= ~SF_INIT;
454			f->extent = ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ?
455					f->size : 0;
456			f->here = 0;
457			f->endb = f->data + f->size;
458			f->next = f->endr = f->endw = f->data;
459			if(f->mode&SF_READ)
460				f->endr = f->endb;
461			else	f->endw = f->endb;
462		}
463		else
464		{	n = f->flags;
465			(void)SFSETBUF(f,f->data,f->size);
466			f->flags |= (n&SF_MALLOC);
467		}
468	}
469
470	if(wanted == (int)SFMODE(f,1))
471		goto done;
472
473	switch(SFMODE(f,1))
474	{
475	case SF_WRITE: /* switching to SF_READ */
476		if(wanted == 0 || wanted == SF_WRITE)
477			break;
478		if(!(f->flags&SF_READ) )
479			goto err_notify;
480		else if(f->flags&SF_STRING)
481		{	SFSTRSIZE(f);
482			f->endb = f->data+f->extent;
483			f->mode = SF_READ;
484			break;
485		}
486
487		/* reset buffer */
488		if(f->next > f->data && SFFLSBUF(f,-1) < 0)
489			goto err_notify;
490
491		if(f->size == 0)
492		{	/* unbuffered */
493			f->data = f->tiny;
494			f->size = sizeof(f->tiny);
495		}
496		f->next = f->endr = f->endw = f->endb = f->data;
497		f->mode = SF_READ|SF_LOCK;
498
499		/* restore saved read data for coprocess */
500		if(f->proc && _sfpmode(f,wanted) < 0)
501			goto err_notify;
502
503		break;
504
505	case (SF_READ|SF_SYNCED): /* a previously sync-ed read stream */
506		if(wanted != SF_WRITE)
507		{	/* just reset the pointers */
508			f->mode = SF_READ|SF_LOCK;
509
510			/* see if must go with new physical location */
511			if((f->flags&(SF_SHARE|SF_PUBLIC)) == (SF_SHARE|SF_PUBLIC) &&
512			   (addr = SFSK(f,0,SEEK_CUR,f->disc)) != f->here)
513			{
514#ifdef MAP_TYPE
515				if((f->bits&SF_MMAP) && f->data)
516				{	SFMUNMAP(f,f->data,f->endb-f->data);
517					f->data = NIL(uchar*);
518				}
519#endif
520				f->endb = f->endr = f->endw = f->next = f->data;
521				f->here = addr;
522			}
523			else
524			{	addr = f->here + (f->endb - f->next);
525				if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
526					goto err_notify;
527				f->here = addr;
528			}
529
530			break;
531		}
532		/* fall thru */
533
534	case SF_READ: /* switching to SF_WRITE */
535		if(wanted != SF_WRITE)
536			break;
537		else if(!(f->flags&SF_WRITE))
538			goto err_notify;
539		else if(f->flags&SF_STRING)
540		{	f->endb = f->data+f->size;
541			f->mode = SF_WRITE|SF_LOCK;
542			break;
543		}
544
545		/* save unread data before switching mode */
546		if(f->proc && _sfpmode(f,wanted) < 0)
547			goto err_notify;
548
549		/* reset buffer and seek pointer */
550		if(!(f->mode&SF_SYNCED) )
551		{	n = f->endb - f->next;
552			if(f->extent >= 0 && (n > 0 || (f->data && (f->bits&SF_MMAP))) )
553			{	/* reset file pointer */
554				addr = f->here - n;
555				if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
556					goto err_notify;
557				f->here = addr;
558			}
559		}
560
561		f->mode = SF_WRITE|SF_LOCK;
562#ifdef MAP_TYPE
563		if(f->bits&SF_MMAP)
564		{	if(f->data)
565				SFMUNMAP(f,f->data,f->endb-f->data);
566			(void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
567		}
568#endif
569		if(f->data == f->tiny)
570		{	f->endb = f->data = f->next = NIL(uchar*);
571			f->size = 0;
572		}
573		else	f->endb = (f->next = f->data) + f->size;
574
575		break;
576
577	default: /* unknown case */
578	err_notify:
579		if((wanted &= SF_RDWR) == 0 && (wanted = f->flags&SF_RDWR) == SF_RDWR)
580			wanted = SF_READ;
581
582		/* set errno for operations that access wrong stream type */
583		if(wanted != (f->mode&SF_RDWR) && f->file >= 0)
584			errno = EBADF;
585
586		if(_Sfnotify) /* notify application of the error */
587			(*_Sfnotify)(f, wanted, (void*)((long)f->file));
588
589		rv = -1;
590		break;
591	}
592
593done:
594	SFOPEN(f,local);
595	return rv;
596}
597