archive_read_support_filter_uu.c revision 228759
1/*-
2 * Copyright (c) 2009 Michihiro NAKAJIMA
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "archive_platform.h"
27__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_compression_uu.c 201248 2009-12-30 06:12:03Z kientzle $");
28
29#ifdef HAVE_ERRNO_H
30#include <errno.h>
31#endif
32#ifdef HAVE_STDLIB_H
33#include <stdlib.h>
34#endif
35#ifdef HAVE_STRING_H
36#include <string.h>
37#endif
38
39#include "archive.h"
40#include "archive_private.h"
41#include "archive_read_private.h"
42
43struct uudecode {
44	int64_t		 total;
45	unsigned char	*in_buff;
46#define IN_BUFF_SIZE	(1024)
47	int		 in_cnt;
48	size_t		 in_allocated;
49	unsigned char	*out_buff;
50#define OUT_BUFF_SIZE	(64 * 1024)
51	int		 state;
52#define ST_FIND_HEAD	0
53#define ST_READ_UU	1
54#define ST_UUEND	2
55#define ST_READ_BASE64	3
56};
57
58static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
59		    struct archive_read_filter *filter);
60static int	uudecode_bidder_init(struct archive_read_filter *);
61
62static ssize_t	uudecode_filter_read(struct archive_read_filter *,
63		    const void **);
64static int	uudecode_filter_close(struct archive_read_filter *);
65
66int
67archive_read_support_compression_uu(struct archive *_a)
68{
69	struct archive_read *a = (struct archive_read *)_a;
70	struct archive_read_filter_bidder *bidder;
71
72	bidder = __archive_read_get_bidder(a);
73	archive_clear_error(_a);
74	if (bidder == NULL)
75		return (ARCHIVE_FATAL);
76
77	bidder->data = NULL;
78	bidder->bid = uudecode_bidder_bid;
79	bidder->init = uudecode_bidder_init;
80	bidder->options = NULL;
81	bidder->free = NULL;
82	return (ARCHIVE_OK);
83}
84
85static const unsigned char ascii[256] = {
86	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
87	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
88	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
89	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
90	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
91	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
92	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
93	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
94	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
95	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
96	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
97	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
98	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
99	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
100	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
101	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
102};
103
104static const unsigned char uuchar[256] = {
105	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
106	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
107	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
108	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
109	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
110	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
111	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
112	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
113	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
114	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
115	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
116	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
117	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
118	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
119	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
120	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
121};
122
123static const unsigned char base64[256] = {
124	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
125	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
126	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
127	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
128	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
129	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
130	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
131	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
132	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
133	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
134	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
135	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
136	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
137	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
138	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
139	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
140};
141
142static const int base64num[128] = {
143	 0,  0,  0,  0,  0,  0,  0,  0,
144	 0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */
145	 0,  0,  0,  0,  0,  0,  0,  0,
146	 0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */
147	 0,  0,  0,  0,  0,  0,  0,  0,
148	 0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */
149	52, 53, 54, 55, 56, 57, 58, 59,
150	60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */
151	 0,  0,  1,  2,  3,  4,  5,  6,
152	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
153	15, 16, 17, 18, 19, 20, 21, 22,
154	23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */
155	 0, 26, 27, 28, 29, 30, 31, 32,
156	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
157	41, 42, 43, 44, 45, 46, 47, 48,
158	49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */
159};
160
161static ssize_t
162get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
163{
164	ssize_t len;
165
166	len = 0;
167	while (len < avail) {
168		switch (ascii[*b]) {
169		case 0:	/* Non-ascii character or control character. */
170			if (nlsize != NULL)
171				*nlsize = 0;
172			return (-1);
173		case '\r':
174			if (avail-len > 1 && b[1] == '\n') {
175				if (nlsize != NULL)
176					*nlsize = 2;
177				return (len+2);
178			}
179			/* FALL THROUGH */
180		case '\n':
181			if (nlsize != NULL)
182				*nlsize = 1;
183			return (len+1);
184		case 1:
185			b++;
186			len++;
187			break;
188		}
189	}
190	if (nlsize != NULL)
191		*nlsize = 0;
192	return (avail);
193}
194
195static ssize_t
196bid_get_line(struct archive_read_filter *filter,
197    const unsigned char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
198{
199	ssize_t len;
200	int quit;
201
202	quit = 0;
203	if (*avail == 0) {
204		*nl = 0;
205		len = 0;
206	} else
207		len = get_line(*b, *avail, nl);
208	/*
209	 * Read bytes more while it does not reach the end of line.
210	 */
211	while (*nl == 0 && len == *avail && !quit) {
212		ssize_t diff = *ravail - *avail;
213
214		*b = __archive_read_filter_ahead(filter, 160 + *ravail, avail);
215		if (*b == NULL) {
216			if (*ravail >= *avail)
217				return (0);
218			/* Reading bytes reaches the end of file. */
219			*b = __archive_read_filter_ahead(filter, *avail, avail);
220			quit = 1;
221		}
222		*ravail = *avail;
223		*b += diff;
224		*avail -= diff;
225		len = get_line(*b, *avail, nl);
226	}
227	return (len);
228}
229
230#define UUDECODE(c) (((c) - 0x20) & 0x3f)
231
232static int
233uudecode_bidder_bid(struct archive_read_filter_bidder *self,
234    struct archive_read_filter *filter)
235{
236	const unsigned char *b;
237	ssize_t avail, ravail;
238	ssize_t len, nl;
239	int l;
240	int firstline;
241
242	(void)self; /* UNUSED */
243
244	b = __archive_read_filter_ahead(filter, 1, &avail);
245	if (b == NULL)
246		return (0);
247
248	firstline = 20;
249	ravail = avail;
250	for (;;) {
251		len = bid_get_line(filter, &b, &avail, &ravail, &nl);
252		if (len < 0 || nl == 0)
253			return (0);/* Binary data. */
254		if (memcmp(b, "begin ", 6) == 0 && len - nl >= 11)
255			l = 6;
256		else if (memcmp(b, "begin-base64 ", 13) == 0 && len - nl >= 18)
257			l = 13;
258		else
259			l = 0;
260
261		if (l > 0 && (b[l] < '0' || b[l] > '7' ||
262		    b[l+1] < '0' || b[l+1] > '7' ||
263		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
264			l = 0;
265
266		b += len;
267		avail -= len;
268		if (l)
269			break;
270		firstline = 0;
271	}
272	if (!avail)
273		return (0);
274	len = bid_get_line(filter, &b, &avail, &ravail, &nl);
275	if (len < 0 || nl == 0)
276		return (0);/* There are non-ascii characters. */
277	avail -= len;
278
279	if (l == 6) {
280		if (!uuchar[*b])
281			return (0);
282		/* Get a length of decoded bytes. */
283		l = UUDECODE(*b++); len--;
284		if (l > 45)
285			/* Normally, maximum length is 45(character 'M'). */
286			return (0);
287		while (l && len-nl > 0) {
288			if (l > 0) {
289				if (!uuchar[*b++])
290					return (0);
291				if (!uuchar[*b++])
292					return (0);
293				len -= 2;
294				--l;
295			}
296			if (l > 0) {
297				if (!uuchar[*b++])
298					return (0);
299				--len;
300				--l;
301			}
302			if (l > 0) {
303				if (!uuchar[*b++])
304					return (0);
305				--len;
306				--l;
307			}
308		}
309		if (len-nl < 0)
310			return (0);
311		if (len-nl == 1 &&
312		    (uuchar[*b] ||		 /* Check sum. */
313		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
314			++b;
315			--len;
316		}
317		b += nl;
318		if (avail && uuchar[*b])
319			return (firstline+30);
320	}
321	if (l == 13) {
322		while (len-nl > 0) {
323			if (!base64[*b++])
324				return (0);
325			--len;
326		}
327		b += nl;
328
329		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
330			return (firstline+40);
331		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
332			return (firstline+40);
333		if (avail > 0 && base64[*b])
334			return (firstline+30);
335	}
336
337	return (0);
338}
339
340static int
341uudecode_bidder_init(struct archive_read_filter *self)
342{
343	struct uudecode   *uudecode;
344	void *out_buff;
345	void *in_buff;
346
347	self->code = ARCHIVE_COMPRESSION_UU;
348	self->name = "uu";
349	self->read = uudecode_filter_read;
350	self->skip = NULL; /* not supported */
351	self->close = uudecode_filter_close;
352
353	uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
354	out_buff = malloc(OUT_BUFF_SIZE);
355	in_buff = malloc(IN_BUFF_SIZE);
356	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
357		archive_set_error(&self->archive->archive, ENOMEM,
358		    "Can't allocate data for uudecode");
359		free(uudecode);
360		free(out_buff);
361		free(in_buff);
362		return (ARCHIVE_FATAL);
363	}
364
365	self->data = uudecode;
366	uudecode->in_buff = in_buff;
367	uudecode->in_cnt = 0;
368	uudecode->in_allocated = IN_BUFF_SIZE;
369	uudecode->out_buff = out_buff;
370	uudecode->state = ST_FIND_HEAD;
371
372	return (ARCHIVE_OK);
373}
374
375static int
376ensure_in_buff_size(struct archive_read_filter *self,
377    struct uudecode *uudecode, size_t size)
378{
379
380	if (size > uudecode->in_allocated) {
381		unsigned char *ptr;
382		size_t newsize;
383
384		/*
385		 * Calculate a new buffer size for in_buff.
386		 * Increase its value until it has enough size we need.
387		 */
388		newsize = uudecode->in_allocated;
389		do {
390			if (newsize < IN_BUFF_SIZE*32)
391				newsize <<= 1;
392			else
393				newsize += IN_BUFF_SIZE;
394		} while (size > newsize);
395		ptr = malloc(newsize);
396		if (ptr == NULL ||
397		    newsize < uudecode->in_allocated) {
398			free(ptr);
399			archive_set_error(&self->archive->archive,
400			    ENOMEM,
401    			    "Can't allocate data for uudecode");
402			return (ARCHIVE_FATAL);
403		}
404		if (uudecode->in_cnt)
405			memmove(ptr, uudecode->in_buff,
406			    uudecode->in_cnt);
407		free(uudecode->in_buff);
408		uudecode->in_buff = ptr;
409		uudecode->in_allocated = newsize;
410	}
411	return (ARCHIVE_OK);
412}
413
414static ssize_t
415uudecode_filter_read(struct archive_read_filter *self, const void **buff)
416{
417	struct uudecode *uudecode;
418	const unsigned char *b, *d;
419	unsigned char *out;
420	ssize_t avail_in, ravail;
421	ssize_t used;
422	ssize_t total;
423	ssize_t len, llen, nl;
424
425	uudecode = (struct uudecode *)self->data;
426
427read_more:
428	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
429	if (d == NULL && avail_in < 0)
430		return (ARCHIVE_FATAL);
431	/* Quiet a code analyzer; make sure avail_in must be zero
432	 * when d is NULL. */
433	if (d == NULL)
434		avail_in = 0;
435	used = 0;
436	total = 0;
437	out = uudecode->out_buff;
438	ravail = avail_in;
439	if (uudecode->in_cnt) {
440		/*
441		 * If there is remaining data which is saved by
442		 * previous calling, use it first.
443		 */
444		if (ensure_in_buff_size(self, uudecode,
445		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
446			return (ARCHIVE_FATAL);
447		memcpy(uudecode->in_buff + uudecode->in_cnt,
448		    d, avail_in);
449		d = uudecode->in_buff;
450		avail_in += uudecode->in_cnt;
451		uudecode->in_cnt = 0;
452	}
453	for (;used < avail_in; d += llen, used += llen) {
454		int l, body;
455
456		b = d;
457		len = get_line(b, avail_in - used, &nl);
458		if (len < 0) {
459			/* Non-ascii character is found. */
460			archive_set_error(&self->archive->archive,
461			    ARCHIVE_ERRNO_MISC,
462			    "Insufficient compressed data");
463			return (ARCHIVE_FATAL);
464		}
465		llen = len;
466		if (nl == 0) {
467			/*
468			 * Save remaining data which does not contain
469			 * NL('\n','\r').
470			 */
471			if (ensure_in_buff_size(self, uudecode, len)
472			    != ARCHIVE_OK)
473				return (ARCHIVE_FATAL);
474			if (uudecode->in_buff != b)
475				memmove(uudecode->in_buff, b, len);
476			uudecode->in_cnt = len;
477			if (total == 0) {
478				/* Do not return 0; it means end-of-file.
479				 * We should try to read bytes more. */
480				__archive_read_filter_consume(
481				    self->upstream, ravail);
482				goto read_more;
483			}
484			break;
485		}
486		if (total + len * 2 > OUT_BUFF_SIZE)
487			break;
488		switch (uudecode->state) {
489		default:
490		case ST_FIND_HEAD:
491			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
492				l = 6;
493			else if (len - nl >= 18 &&
494			    memcmp(b, "begin-base64 ", 13) == 0)
495				l = 13;
496			else
497				l = 0;
498			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
499			    b[l+1] >= '0' && b[l+1] <= '7' &&
500			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
501				if (l == 6)
502					uudecode->state = ST_READ_UU;
503				else
504					uudecode->state = ST_READ_BASE64;
505			}
506			break;
507		case ST_READ_UU:
508			body = len - nl;
509			if (!uuchar[*b] || body <= 0) {
510				archive_set_error(&self->archive->archive,
511				    ARCHIVE_ERRNO_MISC,
512				    "Insufficient compressed data");
513				return (ARCHIVE_FATAL);
514			}
515			/* Get length of undecoded bytes of curent line. */
516			l = UUDECODE(*b++);
517			body--;
518			if (l > body) {
519				archive_set_error(&self->archive->archive,
520				    ARCHIVE_ERRNO_MISC,
521				    "Insufficient compressed data");
522				return (ARCHIVE_FATAL);
523			}
524			if (l == 0) {
525				uudecode->state = ST_UUEND;
526				break;
527			}
528			while (l > 0) {
529				int n = 0;
530
531				if (l > 0) {
532					if (!uuchar[b[0]] || !uuchar[b[1]])
533						break;
534					n = UUDECODE(*b++) << 18;
535					n |= UUDECODE(*b++) << 12;
536					*out++ = n >> 16; total++;
537					--l;
538				}
539				if (l > 0) {
540					if (!uuchar[b[0]])
541						break;
542					n |= UUDECODE(*b++) << 6;
543					*out++ = (n >> 8) & 0xFF; total++;
544					--l;
545				}
546				if (l > 0) {
547					if (!uuchar[b[0]])
548						break;
549					n |= UUDECODE(*b++);
550					*out++ = n & 0xFF; total++;
551					--l;
552				}
553			}
554			if (l) {
555				archive_set_error(&self->archive->archive,
556				    ARCHIVE_ERRNO_MISC,
557				    "Insufficient compressed data");
558				return (ARCHIVE_FATAL);
559			}
560			break;
561		case ST_UUEND:
562			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
563				uudecode->state = ST_FIND_HEAD;
564			else {
565				archive_set_error(&self->archive->archive,
566				    ARCHIVE_ERRNO_MISC,
567				    "Insufficient compressed data");
568				return (ARCHIVE_FATAL);
569			}
570			break;
571		case ST_READ_BASE64:
572			l = len - nl;
573			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
574			    b[2] == '=') {
575				uudecode->state = ST_FIND_HEAD;
576				break;
577			}
578			while (l > 0) {
579				int n = 0;
580
581				if (l > 0) {
582					if (!base64[b[0]] || !base64[b[1]])
583						break;
584					n = base64num[*b++] << 18;
585					n |= base64num[*b++] << 12;
586					*out++ = n >> 16; total++;
587					l -= 2;
588				}
589				if (l > 0) {
590					if (*b == '=')
591						break;
592					if (!base64[*b])
593						break;
594					n |= base64num[*b++] << 6;
595					*out++ = (n >> 8) & 0xFF; total++;
596					--l;
597				}
598				if (l > 0) {
599					if (*b == '=')
600						break;
601					if (!base64[*b])
602						break;
603					n |= base64num[*b++];
604					*out++ = n & 0xFF; total++;
605					--l;
606				}
607			}
608			if (l && *b != '=') {
609				archive_set_error(&self->archive->archive,
610				    ARCHIVE_ERRNO_MISC,
611				    "Insufficient compressed data");
612				return (ARCHIVE_FATAL);
613			}
614			break;
615		}
616	}
617
618	__archive_read_filter_consume(self->upstream, ravail);
619
620	*buff = uudecode->out_buff;
621	uudecode->total += total;
622	return (total);
623}
624
625static int
626uudecode_filter_close(struct archive_read_filter *self)
627{
628	struct uudecode *uudecode;
629
630	uudecode = (struct uudecode *)self->data;
631	free(uudecode->in_buff);
632	free(uudecode->out_buff);
633	free(uudecode);
634
635	return (ARCHIVE_OK);
636}
637
638