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 #include "libarc/url.h"
28 
29 #define DECODEBUFSIZ 255 /* Must be power of 3 */
30 #define INFOBYTES 128
31 
32 typedef struct _URL_hqxdecode
33 {
34     char common[sizeof(struct _URL)];
35     URL reader;
36     long rpos;
37     int beg, end, eof, eod;
38     unsigned char decodebuf[DECODEBUFSIZ];
39     long datalen, rsrclen, restlen;
40     int dsoff, rsoff, zoff;
41     int stage, dataonly, autoclose;
42 } URL_hqxdecode;
43 
44 static long url_hqxdecode_read(URL url, void *buff, long n);
45 static int  url_hqxdecode_fgetc(URL url);
46 static long url_hqxdecode_tell(URL url);
47 static void url_hqxdecode_close(URL url);
48 
url_hqxdecode_open(URL reader,int dataonly,int autoclose)49 URL url_hqxdecode_open(URL reader, int dataonly, int autoclose)
50 {
51     URL_hqxdecode *url;
52 
53     url = (URL_hqxdecode *)alloc_url(sizeof(URL_hqxdecode));
54     if(url == NULL)
55     {
56 	if(autoclose)
57 	    url_close(reader);
58 	url_errno = errno;
59 	return NULL;
60     }
61 
62     /* common members */
63     URLm(url, type)      = URL_hqxdecode_t;
64     URLm(url, url_read)  = url_hqxdecode_read;
65     URLm(url, url_gets)  = NULL;
66     URLm(url, url_fgetc) = url_hqxdecode_fgetc;
67     URLm(url, url_seek)  = NULL;
68     URLm(url, url_tell)  = url_hqxdecode_tell;
69     URLm(url, url_close) = url_hqxdecode_close;
70 
71     /* private members */
72     url->reader = reader;
73     url->rpos = 0;
74     url->beg = 0;
75     url->end = 0;
76     url->eof = url->eod = 0;
77     memset(url->decodebuf, 0, sizeof(url->decodebuf));
78     url->datalen = -1;
79     url->rsrclen = -1;
80     url->restlen = 0;
81     url->stage = 0;
82     url->dataonly = dataonly;
83     url->autoclose = autoclose;
84 
85     return (URL)url;
86 }
87 
hqxgetchar(URL reader)88 static int hqxgetchar(URL reader)
89 {
90     int c;
91     static int hqx_decode_table[256] =
92     {
93 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 	0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
98 	0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x00, 0x00,
99 	0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x00,
100 	0x14, 0x15, EOF,  0x00, 0x00, 0x00, 0x00, 0x00,
101 	0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
102 	0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x00,
103 	0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x00,
104 	0x2c, 0x2d, 0x2e, 0x2f, 0x00, 0x00, 0x00, 0x00,
105 	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00,
106 	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x00, 0x00,
107 	0x3d, 0x3e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00
108     };
109 
110     do
111     {
112 	if((c = url_getc(reader)) == EOF)
113 	    return EOF;
114     } while(c == '\r' || c == '\n');
115     return hqx_decode_table[c];
116 }
117 
hqxdecode_chunk(URL url,unsigned char * p)118 static int hqxdecode_chunk(URL url, unsigned char *p)
119 {
120     int c1, c2, c3, c4;
121     int n;
122 
123     n = 0;
124     if((c1 = hqxgetchar(url)) == EOF)
125 	return 0;
126     if((c2 = hqxgetchar(url)) == EOF)
127 	return 0;
128     p[n++] = ((c1 << 2) | ((c2 & 0x30) >> 4));
129     if((c3 = hqxgetchar(url)) == EOF)
130 	return n;
131     p[n++] = (((c2 & 0xf) << 4) | ((c3 & 0x3c) >> 2));
132     if((c4 = hqxgetchar(url)) == EOF)
133 	return n;
134     p[n++] = (((c3 & 0x03) << 6) | c4);
135     return n;
136 }
137 
convert_int32(unsigned char * p)138 static uint32 convert_int32(unsigned char *p)
139 {
140     return ((uint32)p[3])		|
141 	   (((uint32)p[2]) << 8)	|
142 	   (((uint32)p[1]) << 16)	|
143 	   (((uint32)p[0]) << 24);
144 }
145 
hqxdecode_header(URL_hqxdecode * urlp)146 static int hqxdecode_header(URL_hqxdecode *urlp)
147 {
148     int i, n;
149     unsigned char *p, *q;
150     URL url;
151     int hlen, nlen;
152 
153     n = 0;
154     p = urlp->decodebuf;
155     q = urlp->decodebuf + INFOBYTES;
156     url = urlp->reader;
157     while(n < DECODEBUFSIZ - INFOBYTES - 2)
158     {
159 	i = hqxdecode_chunk(url, q + n);
160 	n += i;
161 	if(i != 3)
162 	{
163 	    urlp->eod = 1;
164 	    break;
165 	}
166     }
167 
168     memset(p, 0, INFOBYTES);
169     nlen = q[0];
170     hlen = nlen + 22;
171 
172     if(n < hlen)
173     {
174 	urlp->eof = 1;
175 	return -1; /* Error */
176     }
177 
178     urlp->datalen = (long)convert_int32(q + hlen - 10);
179     urlp->rsrclen = (long)convert_int32(q + hlen - 6);
180     urlp->dsoff = (((urlp->datalen + 127) >> 7) << 7) - urlp->datalen;
181     urlp->rsoff = (((urlp->rsrclen + 127) >> 7) << 7) - urlp->rsrclen;
182     urlp->zoff = 0;
183 
184     p[1] = nlen;
185     memcpy(p + 2, q + 1, nlen);
186     memcpy(p + 65, q + hlen - 20, 4+4+2); /* type, author, flags */
187     memcpy(p + 83, q + hlen - 10, 4+4);	/* datalen, rsrclen */
188     /* 91: create time (4) */
189     /* 95: modify time (4) */
190 
191     q += hlen;
192     n -= hlen;
193     for(i = 0; i < n; i++)
194 	p[INFOBYTES + i] = q[i];
195     return INFOBYTES + n;
196 }
197 
hqxdecode(URL_hqxdecode * urlp)198 static int hqxdecode(URL_hqxdecode *urlp)
199 {
200     int i, n;
201     unsigned char *p;
202     URL url;
203 
204     if(urlp->eod)
205     {
206 	urlp->eof = 1;
207 	return 1;
208     }
209 
210     if(urlp->stage == 0)
211     {
212 	n = hqxdecode_header(urlp);
213 	if(n == -1)
214 	    return 1;
215 	urlp->end = n;
216 
217 	if(urlp->dataonly)
218 	{
219 	    urlp->beg = INFOBYTES;
220 	    urlp->restlen = urlp->datalen;
221 	}
222 	else
223 	{
224 	    urlp->beg = 0;
225 	    urlp->restlen = urlp->datalen + INFOBYTES;
226 	}
227 
228 	urlp->stage = 1;
229 	return 0;
230     }
231 
232     p = urlp->decodebuf;
233     url = urlp->reader;
234     n = 0;
235 
236     if(urlp->restlen == 0)
237     {
238 	if(urlp->dataonly)
239 	{
240 	    urlp->eof = 1;
241 	    return 1;
242 	}
243 
244 	if(urlp->stage == 2)
245 	{
246 	    urlp->zoff = urlp->rsoff;
247 	    urlp->eof = 1;
248 	    return 1;
249 	}
250 
251 	urlp->zoff = urlp->dsoff;
252 	urlp->stage = 2;
253 
254 	n = urlp->end - urlp->beg;
255 	if(n <= 2)
256 	{
257 	    for(i = 0; i < n; i++)
258 		p[i] = p[i + urlp->beg];
259 	    n += hqxdecode_chunk(url, p + n);
260 	    if(n <= 2)
261 	    {
262 		urlp->eof = 1;
263 		return 1;
264 	    }
265 	    urlp->rpos += urlp->beg;
266 	    urlp->beg = 0;
267 	    urlp->end = n;
268 	}
269 	urlp->restlen = urlp->rsrclen;
270 
271 	/* skip 2 byte (crc) */
272 	urlp->beg += 2;
273 	urlp->rpos -= 2;
274 
275 	n = urlp->beg;
276     }
277 
278     while(n < DECODEBUFSIZ)
279     {
280 	i = hqxdecode_chunk(url, p + n);
281 	n += i;
282 	if(i != 3)
283 	{
284 	    urlp->eod = 1;
285 	    break;
286 	}
287     }
288 
289     urlp->rpos += urlp->beg;
290     urlp->beg = 0;
291     urlp->end = n;
292 
293     if(n == 0)
294     {
295 	urlp->eof = 1;
296 	return 1;
297     }
298 
299     return 0;
300 }
301 
url_hqxdecode_read(URL url,void * buff,long size)302 static long url_hqxdecode_read(URL url, void *buff, long size)
303 {
304     URL_hqxdecode *urlp = (URL_hqxdecode *)url;
305     char *p = (char *)buff;
306     long n;
307     int i;
308 
309     n = 0;
310     while(n < size)
311     {
312 	if(urlp->zoff > 0)
313 	{
314 	    i = urlp->zoff;
315 	    if(i > size - n)
316 		i = size - n;
317 	    memset(p + n, 0, i);
318 	    urlp->zoff -= i;
319 	    urlp->rpos += i;
320 	    n += i;
321 	    continue;
322 	}
323 
324 	if(urlp->eof)
325 	    break;
326 
327 	if(urlp->restlen == 0 || urlp->beg == urlp->end)
328 	{
329 	    hqxdecode(urlp);
330 	    continue;
331 	}
332 
333 	i = urlp->end - urlp->beg;
334 	if(i > urlp->restlen)
335 	    i = urlp->restlen;
336 	if(i > size - n)
337 	    i = size - n;
338 	memcpy(p + n, urlp->decodebuf + urlp->beg, i);
339 	urlp->beg += i;
340 	n += i;
341 	urlp->restlen -= i;
342     }
343 
344     return n;
345 }
346 
url_hqxdecode_fgetc(URL url)347 static int url_hqxdecode_fgetc(URL url)
348 {
349     URL_hqxdecode *urlp = (URL_hqxdecode *)url;
350     int c;
351 
352   retry_read:
353     if(urlp->zoff > 0)
354     {
355 	urlp->zoff--;
356 	urlp->rpos++;
357 	return 0;
358     }
359 
360     if(urlp->eof)
361 	return EOF;
362 
363     if(urlp->restlen == 0 || urlp->beg == urlp->end)
364     {
365 	hqxdecode(urlp);
366 	goto retry_read;
367     }
368 
369     c = (int)urlp->decodebuf[urlp->beg++];
370     urlp->restlen--;
371 
372     return c;
373 }
374 
url_hqxdecode_tell(URL url)375 static long url_hqxdecode_tell(URL url)
376 {
377     URL_hqxdecode *urlp = (URL_hqxdecode *)url;
378 
379     if(urlp->dataonly)
380 	return urlp->rpos + urlp->beg - INFOBYTES;
381     return urlp->rpos + urlp->beg;
382 }
383 
url_hqxdecode_close(URL url)384 static void url_hqxdecode_close(URL url)
385 {
386     URL_hqxdecode *urlp = (URL_hqxdecode *)url;
387 
388     if(urlp->autoclose)
389 	url_close(urlp->reader);
390     free(url);
391 }
392 
393 #ifdef HQXDECODE_MAIN
main(int argc,char ** argv)394 void main(int argc, char** argv)
395 {
396     URL hqxdecoder;
397     char buff[256], *filename;
398     int c;
399 
400     if(argc != 2)
401     {
402 	fprintf(stderr, "Usage: %s hqx-filename\n", argv[0]);
403 	exit(1);
404     }
405     filename = argv[1];
406 
407     if((hqxdecoder = url_file_open(filename)) == NULL)
408     {
409 	perror(argv[1]);
410 	exit(1);
411     }
412 
413     for(;;)
414     {
415 	if(url_readline(hqxdecoder, buff, sizeof(buff)) == NULL)
416 	{
417 	    fprintf(stderr, "%s: Not a hqx-file\n", filename);
418 	    url_close(hqxdecoder);
419 	    exit(1);
420 	}
421 	if((strncmp(buff, "(This file", 10) == 0) ||
422 	   (strncmp(buff, "(Convert with", 13) == 0))
423 	    break;
424     }
425 
426     while((c = url_getc(hqxdecoder)) != EOF)
427 	if(c == ':')
428 	    break;
429     if(c == EOF)
430     {
431 	fprintf(stderr, "%s: Not a hqx-file\n", filename);
432 	url_close(hqxdecoder);
433 	exit(1);
434     }
435 
436     hqxdecoder = url_hqxdecode_open(hqxdecoder, 0, 1);
437 #if HQXDECODE_MAIN
438     while((c = url_getc(hqxdecoder)) != EOF)
439 	putchar(c);
440 #else
441     while((c = url_read(hqxdecoder, buff, sizeof(buff))) > 0)
442 	write(1, buff, c);
443 #endif
444     url_close(hqxdecoder);
445     exit(0);
446 }
447 
448 #endif /* HQXDECODE_MAIN */
449