1 /*
2     Copyright (C) 2000 Masanao Izumo <mo@goice.co.jp>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #include "config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #ifndef NO_STRING_H
23 #include <string.h>
24 #else
25 #include <strings.h>
26 #endif
27 
28 #include "libarc/url.h"
29 
30 #define URL_BUFF_SIZE (8*1024)
31 #define BASESIZE (URL_BUFF_SIZE * 2)
32 #define BASEMASK (BASESIZE-1)
33 
34 typedef struct _URL_buff
35 {
36     char common[sizeof(struct _URL)];
37     URL reader;
38     unsigned char buffer[BASESIZE + URL_BUFF_SIZE]; /* ring buffer */
39     int wp;			/* write pointer to the buffer */
40     int rp;			/* read pointer from the buffer */
41     long pos, posofs;		/* position */
42     int weof;
43     int eof;
44     int autoclose;
45 } URL_buff;
46 
47 static long url_buff_read(URL url, void *buff, long n);
48 static char *url_buff_gets(URL url, char *buff, int n);
49 static int url_buff_fgetc(URL url);
50 static long url_buff_seek(URL url, long offset, int whence);
51 static long url_buff_tell(URL url);
52 static void url_buff_close(URL url);
53 
url_buff_open(URL url,int autoclose)54 URL url_buff_open(URL url, int autoclose)
55 {
56     URL_buff *urlp;
57 
58     if((urlp = (URL_buff *)alloc_url(sizeof(URL_buff))) == NULL)
59     {
60 	if(autoclose)
61 	    url_close(url);
62 	return NULL;
63     }
64 
65     /* common members */
66     URLm(urlp, type)	  = URL_buff_t;
67     URLm(urlp, url_read)  = url_buff_read;
68     URLm(urlp, url_gets)  = url_buff_gets;
69     URLm(urlp, url_fgetc) = url_buff_fgetc;
70     URLm(urlp, url_seek)  = url_buff_seek;
71     URLm(urlp, url_tell)  = url_buff_tell;
72     URLm(urlp, url_close) = url_buff_close;
73 
74     /* private members */
75     urlp->reader = url;
76     memset(urlp->buffer, 0, sizeof(urlp->buffer));
77     urlp->wp = 0;
78     urlp->rp = 0;
79     if((urlp->posofs = url_tell(url)) == -1)
80 	urlp->posofs = 0;
81     urlp->pos = 0;
82     urlp->eof = 0;
83     urlp->autoclose = autoclose;
84 
85     return (URL)urlp;
86 }
87 
prefetch(URL_buff * urlp)88 static void prefetch(URL_buff *urlp)
89 {
90     long i, n;
91 
92     n = url_safe_read(urlp->reader, urlp->buffer + urlp->wp, URL_BUFF_SIZE);
93     if(n <= 0)
94 	return;
95     urlp->wp += n;
96     if(urlp->wp < BASESIZE)
97 	return;
98     if(urlp->wp == BASESIZE)
99     {
100 	urlp->wp = 0;
101 	return;
102     }
103 
104     /* urlp->wp > BASESIZE */
105     i = urlp->wp - BASESIZE;
106     memcpy(urlp->buffer, urlp->buffer + BASESIZE, i);
107     urlp->wp = i;
108 }
109 
url_buff_fgetc(URL url)110 static int url_buff_fgetc(URL url)
111 {
112     URL_buff *urlp = (URL_buff *)url;
113     int c, r;
114 
115     if(urlp->eof)
116 	return EOF;
117 
118     r = urlp->rp;
119     if(r == urlp->wp)
120     {
121 	prefetch(urlp);
122 	if(r == urlp->wp)
123 	{
124 	    urlp->eof = 1;
125 	    return EOF;
126 	}
127     }
128     c = urlp->buffer[r];
129     urlp->rp = ((r + 1) & BASEMASK);
130     urlp->pos++;
131     return c;
132 }
133 
url_buff_read(URL url,void * buff,long n)134 static long url_buff_read(URL url, void *buff, long n)
135 {
136     URL_buff *urlp = (URL_buff *)url;
137     char *s = (char *)buff;
138     int r, i, j;
139 
140     if(urlp->eof)
141 	return 0;
142 
143     r = urlp->rp;
144     if(r == urlp->wp)
145     {
146 	prefetch(urlp);
147 	if(r == urlp->wp)
148 	{
149 	    urlp->eof = 1;
150 	    return EOF;
151 	}
152     }
153 
154     /* first fragment */
155     i = urlp->wp - r;
156     if(i < 0)
157 	i = BASESIZE - r;
158     if(i > n)
159 	i = n;
160     memcpy(s, urlp->buffer + r, i);
161     r = ((r + i) & BASEMASK);
162 
163     if(i == n || r == urlp->wp || r != 0)
164     {
165 	urlp->rp = r;
166 	urlp->pos += i;
167 	return i;
168     }
169 
170     /* second fragment */
171     j = urlp->wp;
172     n -= i;
173     s += i;
174     if(j > n)
175 	j = n;
176     memcpy(s, urlp->buffer, j);
177     urlp->rp = j;
178     urlp->pos += i + j;
179 
180     return i + j;
181 }
182 
url_buff_tell(URL url)183 static long url_buff_tell(URL url)
184 {
185     URL_buff *urlp = (URL_buff *)url;
186 
187     return urlp->pos + urlp->posofs;
188 }
189 
url_buff_gets(URL url,char * buff,int maxsiz)190 static char *url_buff_gets(URL url, char *buff, int maxsiz)
191 {
192     URL_buff *urlp = (URL_buff *)url;
193     int c, r, w;
194     long len, maxlen;
195     int newline = url_newline_code;
196     unsigned char *bp;
197 
198     if(urlp->eof)
199 	return NULL;
200 
201     maxlen = maxsiz - 1;
202     if(maxlen == 0)
203 	*buff = '\0';
204     if(maxlen <= 0)
205 	return buff;
206     len = 0;
207     r = urlp->rp;
208     w = urlp->wp;
209     bp = urlp->buffer;
210 
211     do
212     {
213 	if(r == w)
214 	{
215 	    urlp->wp = w;
216 	    prefetch(urlp);
217 	    w = urlp->wp;
218 	    if(r == w)
219 	    {
220 		urlp->eof = 1;
221 		if(len == 0)
222 		    return NULL;
223 		buff[len] = '\0';
224 		urlp->pos += len;
225 		urlp->rp = r;
226 		return buff;
227 	    }
228 	}
229 	c = bp[r];
230 	buff[len++] = c;
231 	r = ((r + 1) & BASEMASK);
232     } while(c != newline && len < maxlen);
233     buff[len] = '\0';
234     urlp->pos += len;
235     urlp->rp = r;
236     return buff;
237 }
238 
url_buff_seek(URL url,long offset,int whence)239 static long url_buff_seek(URL url, long offset, int whence)
240 {
241     URL_buff *urlp = (URL_buff *)url;
242     long ret, diff, n;
243     int r, w, filled, i;
244 
245     ret = urlp->pos + urlp->posofs;
246     switch(whence)
247     {
248       case SEEK_SET:
249 	diff = offset - ret;
250 	break;
251       case SEEK_CUR:
252 	diff = offset;
253 	break;
254       case SEEK_END:
255 	if(!urlp->eof)
256 	    while(url_buff_fgetc(url) != EOF)
257 		;
258 	diff = offset;
259 	break;
260       default:
261 	url_errno = errno = EPERM;
262 	return -1;
263     }
264 
265     if(diff == 0)
266     {
267 	urlp->eof = 0; /* To be more read */
268 	return ret;
269     }
270 
271     n = 0;			/* number of bytes to move */
272     r = urlp->rp;		/* read pointer */
273     w = urlp->wp;		/* write pointer */
274 
275     if(diff > 0)
276     {
277 	while(diff > 0)
278 	{
279 	    if(r == w)
280 	    {
281 		urlp->wp = w;
282 		prefetch(urlp);
283 		w = urlp->wp;
284 		if(r == w)
285 		{
286 		    urlp->eof = 1;
287 		    urlp->pos += n;
288 		    urlp->rp = r;
289 		    return ret;
290 		}
291 	    }
292 
293 	    i = w - r;
294 	    if(i < 0)
295 		i = BASESIZE - r;
296 	    if(i > diff)
297 		i = diff;
298 	    n += i;
299 	    diff -= i;
300 	    r = ((r + i) & BASEMASK);
301 	}
302 	urlp->pos += n;
303 	urlp->rp = r;
304 	urlp->eof = 0; /* To be more read */
305 	return ret;
306     }
307 
308     /* diff < 0 */
309 
310     diff = -diff;
311     filled = r - w;
312     if(filled <= 0)
313 	filled = BASEMASK + filled;
314     filled--;
315     if(filled > urlp->pos)
316 	filled = urlp->pos;
317 
318     if(filled < diff)
319     {
320 	url_errno = errno = EPERM;
321 	return -1;
322     }
323 
324     /* back `rp' by `diff' */
325     r -= diff;
326     if(r < 0)
327 	r += BASESIZE;
328     urlp->rp = r;
329     urlp->pos -= diff;
330     urlp->eof = 0; /* To be more read */
331     return ret;
332 }
333 
url_buff_close(URL url)334 static void url_buff_close(URL url)
335 {
336     URL_buff *urlp = (URL_buff *)url;
337     if(urlp->autoclose)
338 	url_close(urlp->reader);
339     free(url);
340 }
341