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#include	"sfhdr.h"
23
24/*	Move data from one stream to another.
25**	This code is written so that it'll work even in the presence
26**	of stacking streams, pool, and discipline.
27**	If you must change it, be gentle.
28**
29**	Written by Kiem-Phong Vo.
30*/
31#define MAX_SSIZE	((ssize_t)((~((size_t)0)) >> 1))
32
33#if __STD_C
34Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, reg int rc)
35#else
36Sfoff_t sfmove(fr,fw,n,rc)
37Sfio_t*	fr;	/* moving data from this stream */
38Sfio_t*	fw;	/* moving data to this stream */
39Sfoff_t		n;	/* number of bytes/records to move. <0 for unbounded move */
40reg int		rc;	/* record separator */
41#endif
42{
43	reg uchar	*cp, *next;
44	reg ssize_t	r, w;
45	reg uchar	*endb;
46	reg int		direct;
47	Sfoff_t		n_move, sk, cur;
48	uchar		*rbuf = NIL(uchar*);
49	ssize_t		rsize = 0;
50	SFMTXDECL(fr);	/* declare a shadow stream variable for from stream */
51	SFMTXDECL2(fw);	/* declare a shadow stream variable for to stream */
52
53	SFMTXENTER(fr, (Sfoff_t)0);
54	if(fw)
55		SFMTXBEGIN2(fw, (Sfoff_t)0);
56
57	for(n_move = 0; n != 0; )
58	{
59		if(rc >= 0) /* moving records, let sfgetr() deal with record reading */
60		{	if(!(cp = (uchar*)sfgetr(fr,rc,0)) )
61				n = 0;
62			else
63			{	r = sfvalue(fr);
64				if(fw && (w = SFWRITE(fw, cp, r)) != r)
65				{	if(fr->extent >= 0 )
66						(void)SFSEEK(fr,(Sfoff_t)(-r),SEEK_CUR);
67					if(fw->extent >= 0 && w > 0)
68						(void)SFSEEK(fw,(Sfoff_t)(-w),SEEK_CUR);
69					n = 0;
70				}
71				else
72				{	n_move += 1;
73					if(n > 0)
74						n -= 1;
75				}
76			}
77			continue;
78		}
79
80		/* get the streams into the right mode */
81		if(fr->mode != SF_READ && _sfmode(fr,SF_READ,0) < 0)
82			break;
83
84		SFLOCK(fr,0);
85
86		/* flush the write buffer as necessary to make room */
87		if(fw)
88		{	if(fw->mode != SF_WRITE && _sfmode(fw,SF_WRITE,0) < 0 )
89				break;
90			SFLOCK(fw,0);
91			if(fw->next >= fw->endb ||
92			   (fw->next > fw->data && fr->extent < 0 &&
93			    (fw->extent < 0 || (fw->flags&SF_SHARE)) ) )
94				if(SFFLSBUF(fw,-1) < 0 )
95					break;
96		}
97		else if((cur = SFSEEK(fr, (Sfoff_t)0, SEEK_CUR)) >= 0 )
98		{	sk = n > 0 ? SFSEEK(fr, n, SEEK_CUR) : SFSEEK(fr, 0, SEEK_END);
99			if(sk > cur) /* safe to skip over data in current stream */
100			{	n_move += sk - cur;
101				if(n > 0)
102					n -= sk - cur;
103				continue;
104			}
105			/* else: stream unstacking may happen below */
106		}
107
108		/* about to move all, set map to a large amount */
109		if(n < 0 && (fr->bits&SF_MMAP) && !(fr->bits&SF_MVSIZE) )
110		{	SFMVSET(fr);
111			fr->bits |= SF_SEQUENTIAL; /* sequentially access data */
112		}
113
114		/* try reading a block of data */
115		direct = 0;
116		if((r = fr->endb - (next = fr->next)) <= 0)
117		{	/* amount of data remained to be read */
118			if((w = n > MAX_SSIZE ? MAX_SSIZE : (ssize_t)n) < 0)
119			{	if(fr->extent < 0)
120					w = fr->data == fr->tiny ? SF_GRAIN : fr->size;
121				else if((fr->extent-fr->here) > SF_NMAP*SF_PAGE)
122					w = SF_NMAP*SF_PAGE;
123				else	w = (ssize_t)(fr->extent-fr->here);
124			}
125
126			/* use a decent buffer for data transfer but make sure
127			   that if we overread, the left over can be retrieved
128			*/
129			if(!(fr->flags&SF_STRING) && !(fr->bits&SF_MMAP) &&
130			   (n < 0 || fr->extent >= 0) )
131			{	reg ssize_t maxw = 4*(_Sfpage > 0 ? _Sfpage : SF_PAGE);
132
133				/* direct transfer to a seekable write stream */
134				if(fw && fw->extent >= 0 && w <= (fw->endb-fw->next) )
135				{	w = fw->endb - (next = fw->next);
136					direct = SF_WRITE;
137				}
138				else if(w > fr->size && maxw > fr->size)
139				{	/* making our own buffer */
140					if(w >= maxw)
141						w = maxw;
142					else	w = ((w+fr->size-1)/fr->size)*fr->size;
143					if(rsize <= 0 && (rbuf = (uchar*)malloc(w)) )
144						rsize = w;
145					if(rbuf)
146					{	next = rbuf;
147						w = rsize;
148						direct = SF_STRING;
149					}
150				}
151			}
152
153			if(!direct)
154			{	/* make sure we don't read too far ahead */
155				if(n > 0 && fr->extent < 0 && (fr->flags&SF_SHARE) )
156				{	if((Sfoff_t)(r = fr->size) > n)
157						r = (ssize_t)n;
158				}
159				else	r = -1;
160				if((r = SFFILBUF(fr,r)) <= 0)
161					break;
162				next = fr->next;
163			}
164			else
165			{	/* actual amount to be read */
166				if(n > 0 && n < w)
167					w = (ssize_t)n;
168
169				if((r = SFRD(fr,next,w,fr->disc)) > 0)
170					fr->next = fr->endb = fr->endr = fr->data;
171				else if(r == 0)
172					break;		/* eof */
173				else	goto again;	/* popped stack */
174			}
175		}
176
177		/* compute the extent of data to be moved */
178		endb = next+r;
179		if(n > 0)
180		{	if(r > n)
181				r = (ssize_t)n;
182			n -= r;
183		}
184		n_move += r;
185		cp = next+r;
186
187		if(!direct)
188			fr->next += r;
189		else if((w = endb-cp) > 0)
190		{	/* move left-over to read stream */
191			if(w > fr->size)
192				w = fr->size;
193			memcpy((Void_t*)fr->data,(Void_t*)cp,w);
194			fr->endb = fr->data+w;
195			if((w = endb - (cp+w)) > 0)
196				(void)SFSK(fr,(Sfoff_t)(-w),SEEK_CUR,fr->disc);
197		}
198
199		if(fw)
200		{	if(direct == SF_WRITE)
201				fw->next += r;
202			else if(r <= (fw->endb-fw->next) )
203			{	memcpy((Void_t*)fw->next,(Void_t*)next,r);
204				fw->next += r;
205			}
206			else if((w = SFWRITE(fw,(Void_t*)next,r)) != r)
207			{	/* a write error happened */
208				if(w > 0)
209				{	r -= w;
210					n_move -= r;
211				}
212				if(fr->extent >= 0)
213					(void)SFSEEK(fr,(Sfoff_t)(-r),SEEK_CUR);
214				break;
215			}
216		}
217
218	again:
219		SFOPEN(fr,0);
220		if(fw)
221			SFOPEN(fw,0);
222	}
223
224	if(n < 0 && (fr->bits&SF_MMAP) && (fr->bits&SF_MVSIZE))
225	{	/* back to normal access mode */
226		SFMVUNSET(fr);
227		if((fr->bits&SF_SEQUENTIAL) && (fr->data))
228			SFMMSEQOFF(fr,fr->data,fr->endb-fr->data);
229		fr->bits &= ~SF_SEQUENTIAL;
230	}
231
232	if(rbuf)
233		free(rbuf);
234
235	if(fw)
236	{	SFOPEN(fw,0);
237		SFMTXEND2(fw);
238	}
239
240	SFOPEN(fr,0);
241	SFMTXRETURN(fr, n_move);
242}
243