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	"sfdchdr.h"
23
24/*	Discipline to make an unseekable read stream seekable
25**
26**	sfraise(f,SFSK_DISCARD,0) discards previous seek data
27**	but seeks from current offset on still allowed
28**
29**	Written by Kiem-Phong Vo, kpv@research.att.com, 03/18/1998.
30*/
31
32typedef struct _skable_s
33{	Sfdisc_t	disc;	/* sfio discipline */
34	Sfio_t*		shadow;	/* to shadow data */
35	Sfoff_t		discard;/* sfseek(f,-1,SEEK_SET) discarded data */
36	Sfoff_t		extent; /* shadow extent */
37	int		eof;	/* if eof has been reached */
38} Seek_t;
39
40#if __STD_C
41static ssize_t skwrite(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* disc)
42#else
43static ssize_t skwrite(f, buf, n, disc)
44Sfio_t*		f;	/* stream involved */
45Void_t*		buf;	/* buffer to read into */
46size_t		n;	/* number of bytes to read */
47Sfdisc_t*	disc;	/* discipline */
48#endif
49{
50	return (ssize_t)(-1);
51}
52
53#if __STD_C
54static ssize_t skread(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc)
55#else
56static ssize_t skread(f, buf, n, disc)
57Sfio_t*		f;	/* stream involved */
58Void_t*		buf;	/* buffer to read into */
59size_t		n;	/* number of bytes to read */
60Sfdisc_t*	disc;	/* discipline */
61#endif
62{
63	Seek_t*		sk;
64	Sfio_t*		sf;
65	Sfoff_t		addr;
66	ssize_t		r, w, p;
67
68	sk = (Seek_t*)disc;
69	sf = sk->shadow;
70	if(sk->eof)
71		return sfread(sf,buf,n);
72
73	addr = sfseek(sf,(Sfoff_t)0,SEEK_CUR);
74
75	if(addr+n <= sk->extent)
76		return sfread(sf,buf,n);
77
78	if((r = (ssize_t)(sk->extent-addr)) > 0)
79	{	if((w = sfread(sf,buf,r)) != r)
80			return w;
81		buf = (char*)buf + r;
82		n -= r;
83	}
84
85	/* do a raw read */
86	if((w = sfrd(f,buf,n,disc)) <= 0)
87	{	sk->eof = 1;
88		w = 0;
89	}
90	else
91	{
92		if((p = sfwrite(sf,buf,w)) != w)
93			sk->eof = 1;
94		if(p > 0)
95			sk->extent += p;
96	}
97
98	return r+w;
99}
100
101#if __STD_C
102static Sfoff_t skseek(Sfio_t* f, Sfoff_t addr, int type, Sfdisc_t* disc)
103#else
104static Sfoff_t skseek(f, addr, type, disc)
105Sfio_t*		f;
106Sfoff_t		addr;
107int		type;
108Sfdisc_t*	disc;
109#endif
110{
111	Seek_t*		sk;
112	Sfio_t*		sf;
113	char		buf[SF_BUFSIZE];
114	ssize_t		r, w;
115
116	sk = (Seek_t*)disc;
117	sf = sk->shadow;
118
119	switch (type)
120	{
121	case SEEK_SET:
122		addr -= sk->discard;
123		break;
124	case SEEK_CUR:
125		addr += sftell(sf);
126		break;
127	case SEEK_END:
128		addr += sk->extent;
129		break;
130	default:
131		return -1;
132	}
133
134	if(addr < 0)
135		return (Sfoff_t)(-1);
136	else if(addr > sk->extent)
137	{	if(sk->eof)
138			return (Sfoff_t)(-1);
139
140		/* read enough to reach the seek point */
141		while(addr > sk->extent)
142		{	if(addr > sk->extent+sizeof(buf) )
143				w = sizeof(buf);
144			else	w = (int)(addr-sk->extent);
145			if((r = sfrd(f,buf,w,disc)) <= 0)
146				w = r-1;
147			else if((w = sfwrite(sf,buf,r)) > 0)
148				sk->extent += w;
149			if(w != r)
150			{	sk->eof = 1;
151				break;
152			}
153		}
154
155		if(addr > sk->extent)
156			return (Sfoff_t)(-1);
157	}
158
159	return sfseek(sf,addr,SEEK_SET) + sk->discard;
160}
161
162/* on close, remove the discipline */
163#if __STD_C
164static int skexcept(Sfio_t* f, int type, Void_t* data, Sfdisc_t* disc)
165#else
166static int skexcept(f,type,data,disc)
167Sfio_t*		f;
168int		type;
169Void_t*		data;
170Sfdisc_t*	disc;
171#endif
172{
173	Seek_t*		sk;
174
175	sk = (Seek_t*)disc;
176
177	switch (type)
178	{
179	case SF_FINAL:
180	case SF_DPOP:
181		sfclose(sk->shadow);
182		free(disc);
183		break;
184	case SFSK_DISCARD:
185		sk->eof = 0;
186		sk->discard += sk->extent;
187		sk->extent = 0;
188		sfseek(sk->shadow,(Sfoff_t)0,SEEK_SET);
189		break;
190	}
191	return 0;
192}
193
194#if __STD_C
195int sfdcseekable(Sfio_t* f)
196#else
197int sfdcseekable(f)
198Sfio_t*	f;
199#endif
200{
201	reg Seek_t*	sk;
202
203	/* see if already seekable */
204	if(sfseek(f,(Sfoff_t)0,SEEK_CUR) >= 0)
205		return 0;
206
207	if(!(sk = (Seek_t*)malloc(sizeof(Seek_t))) )
208		return -1;
209	memset(sk, 0, sizeof(*sk));
210
211	sk->disc.readf = skread;
212	sk->disc.writef = skwrite;
213	sk->disc.seekf = skseek;
214	sk->disc.exceptf = skexcept;
215	sk->shadow = sftmp(SF_BUFSIZE);
216	sk->discard = 0;
217	sk->extent = 0;
218	sk->eof = 0;
219
220	if(sfdisc(f, (Sfdisc_t*)sk) != (Sfdisc_t*)sk)
221	{	sfclose(sk->shadow);
222		free(sk);
223		return -1;
224	}
225
226	return 0;
227}
228