1/***********************************************************************
2*                                                                      *
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                     *
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->bits&SF_MMAP) && f->data) ||
102			   ((f->mode&SF_WRITE) && f->next == f->data) )
103				(void)SFSETBUF(f,NIL(Void_t*),0);
104			f->mode |= pool;
105
106			SFMTXUNLOCK(f);
107			SFOPEN(f,0);
108		}
109	}
110}
111
112/* put into discrete pool */
113#if __STD_C
114int _sfsetpool(Sfio_t* f)
115#else
116int _sfsetpool(f)
117Sfio_t*	f;
118#endif
119{
120	reg Sfpool_t*	p;
121	reg Sfio_t**	array;
122	reg int		n, rv;
123
124	if(!_Sfcleanup)
125	{	_Sfcleanup = _sfcleanup;
126		(void)atexit(_sfcleanup);
127	}
128
129	if(!(p = f->pool) )
130		p = f->pool = &_Sfpool;
131
132	POOLMTXENTER(p);
133
134	rv = -1;
135
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]);
139			p->sf = p->array;
140		}
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*))) )
144				goto done;
145
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);
150
151			p->sf = array;
152			p->s_sf = n;
153		}
154	}
155
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.
158	*/
159	p->sf[p->n_sf++] = f;
160	rv = 0;
161
162done:
163	POOLMTXRETURN(p, rv);
164}
165
166/* create an auxiliary buffer for sfgetr/sfreserve/sfputr */
167#if __STD_C
168Sfrsrv_t* _sfrsrv(reg Sfio_t* f, reg ssize_t size)
169#else
170Sfrsrv_t* _sfrsrv(f,size)
171reg Sfio_t*	f;
172reg ssize_t	size;
173#endif
174{
175	Sfrsrv_t	*rsrv, *rs;
176
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))))
181			size = -1;
182		else
183		{	if(rsrv)
184			{	if(rsrv->slen > 0)
185					memcpy(rs,rsrv,sizeof(Sfrsrv_t)+rsrv->slen);
186				free(rsrv);
187			}
188			f->rsrv = rsrv = rs;
189			rsrv->size = size;
190			rsrv->slen = 0;
191		}
192	}
193
194	if(rsrv && size > 0)
195		rsrv->slen = 0;
196
197	return size >= 0 ? rsrv : NIL(Sfrsrv_t*);
198}
199
200#ifdef SIGPIPE
201#if __STD_C
202static void ignoresig(int sig)
203#else
204static void ignoresig(sig)
205int sig;
206#endif
207{
208	signal(sig, ignoresig);
209}
210#endif
211
212#if __STD_C
213int _sfpopen(reg Sfio_t* f, int fd, int pid, int stdio)
214#else
215int _sfpopen(f, fd, pid, stdio)
216reg Sfio_t*	f;
217int		fd;
218int		pid;
219int		stdio;	/* stdio popen() does not reset SIGPIPE handler */
220#endif
221{
222	reg Sfproc_t*	p;
223
224	if(f->proc)
225		return 0;
226
227	if(!(p = f->proc = (Sfproc_t*)malloc(sizeof(Sfproc_t))) )
228		return -1;
229
230	p->pid = pid;
231	p->size = p->ndata = 0;
232	p->rdata = NIL(uchar*);
233	p->file = fd;
234	p->sigp = (!stdio && pid >= 0 && (f->flags&SF_WRITE)) ? 1 : 0;
235
236#ifdef SIGPIPE	/* protect from broken pipe signal */
237	if(p->sigp)
238	{	Sfsignal_f	handler;
239
240		(void)vtmtxlock(_Sfmutex);
241		if((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL &&
242		    handler != ignoresig)
243			signal(SIGPIPE, handler); /* honor user handler */
244		_Sfsigp += 1;
245		(void)vtmtxunlock(_Sfmutex);
246	}
247#endif
248
249	return 0;
250}
251
252#if __STD_C
253int _sfpclose(reg Sfio_t* f)
254#else
255int _sfpclose(f)
256reg Sfio_t*	f;	/* stream to close */
257#endif
258{
259	Sfproc_t*	p;
260	int		pid, status;
261
262	if(!(p = f->proc))
263		return -1;
264	f->proc = NIL(Sfproc_t*);
265
266	if(p->rdata)
267		free(p->rdata);
268
269	if(p->pid < 0)
270		status = 0;
271	else
272	{	/* close the associated stream */
273		if(p->file >= 0)
274			CLOSE(p->file);
275
276		/* wait for process termination */
277#if _PACKAGE_ast
278		sigcritical(SIG_REG_EXEC|SIG_REG_PROC);
279#endif
280		while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR)
281			;
282		if(pid == -1)
283			status = -1;
284#if _PACKAGE_ast
285		sigcritical(0);
286#endif
287
288#ifdef SIGPIPE
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 */
295			_Sfsigp = 0;
296		}
297		(void)vtmtxunlock(_Sfmutex);
298#endif
299	}
300
301	free(p);
302	return status;
303}
304
305#if __STD_C
306static int _sfpmode(Sfio_t* f, int type)
307#else
308static int _sfpmode(f,type)
309Sfio_t*	f;
310int	type;
311#endif
312{
313	Sfproc_t*	p;
314
315	if(!(p = f->proc) )
316		return -1;
317
318	if(type == SF_WRITE)
319	{	/* save unread data */
320		p->ndata = f->endb-f->next;
321		if(p->ndata > p->size)
322		{	if(p->rdata)
323				free((char*)p->rdata);
324			if((p->rdata = (uchar*)malloc(p->ndata)) )
325				p->size = p->ndata;
326			else
327			{	p->size = 0;
328				return -1;
329			}
330		}
331		if(p->ndata > 0)
332			memcpy((Void_t*)p->rdata,(Void_t*)f->next,p->ndata);
333		f->endb = f->data;
334	}
335	else
336	{	/* restore read data */
337		if(p->ndata > f->size)	/* may lose data!!! */
338			p->ndata = f->size;
339		if(p->ndata > 0)
340		{	memcpy((Void_t*)f->data,(Void_t*)p->rdata,p->ndata);
341			f->endb = f->data+p->ndata;
342			p->ndata = 0;
343		}
344	}
345
346	/* switch file descriptor */
347	if(p->pid >= 0)
348	{	type = f->file;
349		f->file = p->file;
350		p->file = type;
351	}
352
353	return 0;
354}
355
356#if __STD_C
357int _sfmode(reg Sfio_t* f, reg int wanted, reg int local)
358#else
359int _sfmode(f, wanted, local)
360reg Sfio_t*	f;	/* change r/w mode and sync file pointer for this stream */
361reg int		wanted;	/* desired mode */
362reg int		local;	/* a local call */
363#endif
364{
365	reg int	n;
366	Sfoff_t	addr;
367	reg int	rv = 0;
368
369	SFONCE();	/* initialize mutexes */
370
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;
376		}
377	}
378
379	if((!local && SFFROZEN(f)) || (!(f->flags&SF_STRING) && f->file < 0))
380	{	if(local || !f->disc || !f->disc->exceptf)
381		{	local = 1;
382			goto err_notify;
383		}
384
385		for(;;)
386		{	if((rv = (*f->disc->exceptf)(f,SF_LOCKED,0,f->disc)) < 0)
387				return rv;
388			if((!local && SFFROZEN(f)) ||
389			   (!(f->flags&SF_STRING) && f->file < 0) )
390			{	if(rv == 0)
391				{	local = 1;
392					goto err_notify;
393				}
394				else	continue;
395			}
396			else	break;
397		}
398	}
399
400	if(f->mode&SF_GETR)
401	{	f->mode &= ~SF_GETR;
402#ifdef MAP_TYPE
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);
406			f->tiny[0] = 0;
407		}
408		else
409#endif
410		if(f->getr)
411		{	f->next[-1] = f->getr;
412			f->getr = 0;
413		}
414	}
415
416	if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */
417		(*_Sfstdsync)(f);
418
419	if(f->disc == _Sfudisc && wanted == SF_WRITE &&
420	   sfclose((*_Sfstack)(f,NIL(Sfio_t*))) < 0 )
421	{	local = 1;
422		goto err_notify;
423	}
424
425	if(f->mode&SF_POOL)
426	{	/* move to head of pool */
427		if(f == f->pool->sf[0] || (*_Sfpmove)(f,0) < 0 )
428		{	local = 1;
429			goto err_notify;
430		}
431		f->mode &= ~SF_POOL;
432	}
433
434	SFLOCK(f,local);
435
436	/* buffer initialization */
437	wanted &= SF_RDWR;
438	if(f->mode&SF_INIT)
439	{
440		if(!f->pool && _sfsetpool(f) < 0)
441		{	rv = -1;
442			goto done;
443		}
444
445		if(wanted == 0)
446			goto done;
447
448		if(wanted != (int)(f->mode&SF_RDWR) && !(f->flags&wanted) )
449			goto err_notify;
450
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)) ?
454					f->size : 0;
455			f->here = 0;
456			f->endb = f->data + f->size;
457			f->next = f->endr = f->endw = f->data;
458			if(f->mode&SF_READ)
459				f->endr = f->endb;
460			else	f->endw = f->endb;
461		}
462		else
463		{	n = f->flags;
464			(void)SFSETBUF(f,f->data,f->size);
465			f->flags |= (n&SF_MALLOC);
466		}
467	}
468
469	if(wanted == (int)SFMODE(f,1))
470		goto done;
471
472	switch(SFMODE(f,1))
473	{
474	case SF_WRITE: /* switching to SF_READ */
475		if(wanted == 0 || wanted == SF_WRITE)
476			break;
477		if(!(f->flags&SF_READ) )
478			goto err_notify;
479		else if(f->flags&SF_STRING)
480		{	SFSTRSIZE(f);
481			f->endb = f->data+f->extent;
482			f->mode = SF_READ;
483			break;
484		}
485
486		/* reset buffer */
487		if(f->next > f->data && SFFLSBUF(f,-1) < 0)
488			goto err_notify;
489
490		if(f->size == 0)
491		{	/* unbuffered */
492			f->data = f->tiny;
493			f->size = sizeof(f->tiny);
494		}
495		f->next = f->endr = f->endw = f->endb = f->data;
496		f->mode = SF_READ|SF_LOCK;
497
498		/* restore saved read data for coprocess */
499		if(f->proc && _sfpmode(f,wanted) < 0)
500			goto err_notify;
501
502		break;
503
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;
508
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)
512			{
513#ifdef MAP_TYPE
514				if((f->bits&SF_MMAP) && f->data)
515				{	SFMUNMAP(f,f->data,f->endb-f->data);
516					f->data = NIL(uchar*);
517				}
518#endif
519				f->endb = f->endr = f->endw = f->next = f->data;
520				f->here = addr;
521			}
522			else
523			{	addr = f->here + (f->endb - f->next);
524				if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
525					goto err_notify;
526				f->here = addr;
527			}
528
529			break;
530		}
531		/* fall thru */
532
533	case SF_READ: /* switching to SF_WRITE */
534		if(wanted != SF_WRITE)
535			break;
536		else if(!(f->flags&SF_WRITE))
537			goto err_notify;
538		else if(f->flags&SF_STRING)
539		{	f->endb = f->data+f->size;
540			f->mode = SF_WRITE|SF_LOCK;
541			break;
542		}
543
544		/* save unread data before switching mode */
545		if(f->proc && _sfpmode(f,wanted) < 0)
546			goto err_notify;
547
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 */
553				addr = f->here - n;
554				if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
555					goto err_notify;
556				f->here = addr;
557			}
558		}
559
560		f->mode = SF_WRITE|SF_LOCK;
561#ifdef MAP_TYPE
562		if(f->bits&SF_MMAP)
563		{	if(f->data)
564				SFMUNMAP(f,f->data,f->endb-f->data);
565			(void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
566		}
567#endif
568		if(f->data == f->tiny)
569		{	f->endb = f->data = f->next = NIL(uchar*);
570			f->size = 0;
571		}
572		else	f->endb = (f->next = f->data) + f->size;
573
574		break;
575
576	default: /* unknown case */
577	err_notify:
578		if((wanted &= SF_RDWR) == 0 && (wanted = f->flags&SF_RDWR) == SF_RDWR)
579			wanted = SF_READ;
580
581		/* set errno for operations that access wrong stream type */
582		if(wanted != (f->mode&SF_RDWR) && f->file >= 0)
583			errno = EBADF;
584
585		if(_Sfnotify) /* notify application of the error */
586			(*_Sfnotify)(f, wanted, (void*)((long)f->file));
587
588		rv = -1;
589		break;
590	}
591
592done:
593	SFOPEN(f,local);
594	return rv;
595}
596