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		newsize = uudecode->in_allocated << 1;
385		ptr = malloc(newsize);
386		if (ptr == NULL ||
387		    newsize < uudecode->in_allocated) {
388			free(ptr);
389			archive_set_error(&self->archive->archive,
390			    ENOMEM,
391    			    "Can't allocate data for uudecode");
392			return (ARCHIVE_FATAL);
393		}
394		if (uudecode->in_cnt)
395			memmove(ptr, uudecode->in_buff,
396			    uudecode->in_cnt);
397		free(uudecode->in_buff);
398		uudecode->in_buff = ptr;
399		uudecode->in_allocated = newsize;
400	}
401	return (ARCHIVE_OK);
402}
403
404static ssize_t
405uudecode_filter_read(struct archive_read_filter *self, const void **buff)
406{
407	struct uudecode *uudecode;
408	const unsigned char *b, *d;
409	unsigned char *out;
410	ssize_t avail_in, ravail;
411	ssize_t used;
412	ssize_t total;
413	ssize_t len, llen, nl;
414
415	uudecode = (struct uudecode *)self->data;
416
417read_more:
418	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
419	if (d == NULL && avail_in < 0)
420		return (ARCHIVE_FATAL);
421	/* Quiet a code analyzer; make sure avail_in must be zero
422	 * when d is NULL. */
423	if (d == NULL)
424		avail_in = 0;
425	used = 0;
426	total = 0;
427	out = uudecode->out_buff;
428	ravail = avail_in;
429	if (uudecode->in_cnt) {
430		/*
431		 * If there is remaining data which is saved by
432		 * previous calling, use it first.
433		 */
434		if (ensure_in_buff_size(self, uudecode,
435		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
436			return (ARCHIVE_FATAL);
437		memcpy(uudecode->in_buff + uudecode->in_cnt,
438		    d, avail_in);
439		d = uudecode->in_buff;
440		avail_in += uudecode->in_cnt;
441		uudecode->in_cnt = 0;
442	}
443	for (;used < avail_in; d += llen, used += llen) {
444		int l, body;
445
446		b = d;
447		len = get_line(b, avail_in - used, &nl);
448		if (len < 0) {
449			/* Non-ascii character is found. */
450			archive_set_error(&self->archive->archive,
451			    ARCHIVE_ERRNO_MISC,
452			    "Insufficient compressed data");
453			return (ARCHIVE_FATAL);
454		}
455		llen = len;
456		if (nl == 0) {
457			/*
458			 * Save remaining data which does not contain
459			 * NL('\n','\r').
460			 */
461			if (ensure_in_buff_size(self, uudecode, len)
462			    != ARCHIVE_OK)
463				return (ARCHIVE_FATAL);
464			if (uudecode->in_buff != b)
465				memmove(uudecode->in_buff, b, len);
466			uudecode->in_cnt = len;
467			if (total == 0) {
468				/* Do not return 0; it means end-of-file.
469				 * We should try to read bytes more. */
470				__archive_read_filter_consume(
471				    self->upstream, ravail);
472				goto read_more;
473			}
474			break;
475		}
476		if (total + len * 2 > OUT_BUFF_SIZE)
477			break;
478		switch (uudecode->state) {
479		default:
480		case ST_FIND_HEAD:
481			if (len - nl > 13 && memcmp(b, "begin ", 6) == 0)
482				l = 6;
483			else if (len - nl > 18 &&
484			    memcmp(b, "begin-base64 ", 13) == 0)
485				l = 13;
486			else
487				l = 0;
488			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
489			    b[l+1] >= '0' && b[l+1] <= '7' &&
490			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
491				if (l == 6)
492					uudecode->state = ST_READ_UU;
493				else
494					uudecode->state = ST_READ_BASE64;
495			}
496			break;
497		case ST_READ_UU:
498			body = len - nl;
499			if (!uuchar[*b] || body <= 0) {
500				archive_set_error(&self->archive->archive,
501				    ARCHIVE_ERRNO_MISC,
502				    "Insufficient compressed data");
503				return (ARCHIVE_FATAL);
504			}
505			/* Get length of undecoded bytes of curent line. */
506			l = UUDECODE(*b++);
507			body--;
508			if (l > body) {
509				archive_set_error(&self->archive->archive,
510				    ARCHIVE_ERRNO_MISC,
511				    "Insufficient compressed data");
512				return (ARCHIVE_FATAL);
513			}
514			if (l == 0) {
515				uudecode->state = ST_UUEND;
516				break;
517			}
518			while (l > 0) {
519				int n = 0;
520
521				if (l > 0) {
522					if (!uuchar[b[0]] || !uuchar[b[1]])
523						break;
524					n = UUDECODE(*b++) << 18;
525					n |= UUDECODE(*b++) << 12;
526					*out++ = n >> 16; total++;
527					--l;
528				}
529				if (l > 0) {
530					if (!uuchar[b[0]])
531						break;
532					n |= UUDECODE(*b++) << 6;
533					*out++ = (n >> 8) & 0xFF; total++;
534					--l;
535				}
536				if (l > 0) {
537					if (!uuchar[b[0]])
538						break;
539					n |= UUDECODE(*b++);
540					*out++ = n & 0xFF; total++;
541					--l;
542				}
543			}
544			if (l) {
545				archive_set_error(&self->archive->archive,
546				    ARCHIVE_ERRNO_MISC,
547				    "Insufficient compressed data");
548				return (ARCHIVE_FATAL);
549			}
550			break;
551		case ST_UUEND:
552			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
553				uudecode->state = ST_FIND_HEAD;
554			else {
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_READ_BASE64:
562			l = len - nl;
563			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
564			    b[2] == '=') {
565				uudecode->state = ST_FIND_HEAD;
566				break;
567			}
568			while (l > 0) {
569				int n = 0;
570
571				if (l > 0) {
572					if (!base64[b[0]] || !base64[b[1]])
573						break;
574					n = base64num[*b++] << 18;
575					n |= base64num[*b++] << 12;
576					*out++ = n >> 16; total++;
577					l -= 2;
578				}
579				if (l > 0) {
580					if (*b == '=')
581						break;
582					if (!base64[*b])
583						break;
584					n |= base64num[*b++] << 6;
585					*out++ = (n >> 8) & 0xFF; total++;
586					--l;
587				}
588				if (l > 0) {
589					if (*b == '=')
590						break;
591					if (!base64[*b])
592						break;
593					n |= base64num[*b++];
594					*out++ = n & 0xFF; total++;
595					--l;
596				}
597			}
598			if (l && *b != '=') {
599				archive_set_error(&self->archive->archive,
600				    ARCHIVE_ERRNO_MISC,
601				    "Insufficient compressed data");
602				return (ARCHIVE_FATAL);
603			}
604			break;
605		}
606	}
607
608	__archive_read_filter_consume(self->upstream, ravail);
609
610	*buff = uudecode->out_buff;
611	uudecode->total += total;
612	return (total);
613}
614
615static int
616uudecode_filter_close(struct archive_read_filter *self)
617{
618	struct uudecode *uudecode;
619
620	uudecode = (struct uudecode *)self->data;
621	free(uudecode->in_buff);
622	free(uudecode->out_buff);
623	free(uudecode);
624
625	return (ARCHIVE_OK);
626}
627
628