Import libarchive-2.8.5.
[dragonfly.git] / contrib / libarchive / libarchive / archive_read_support_compression_uu.c
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
43 struct 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
58 static int      uudecode_bidder_bid(struct archive_read_filter_bidder *,
59                     struct archive_read_filter *filter);
60 static int      uudecode_bidder_init(struct archive_read_filter *);
61
62 static ssize_t  uudecode_filter_read(struct archive_read_filter *,
63                     const void **);
64 static int      uudecode_filter_close(struct archive_read_filter *);
65
66 int
67 archive_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
85 static 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
104 static 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
123 static 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
142 static 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
161 static ssize_t
162 get_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
195 static ssize_t
196 bid_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
232 static int
233 uudecode_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
340 static int
341 uudecode_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
375 static int
376 ensure_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
414 static ssize_t
415 uudecode_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
427 read_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
625 static int
626 uudecode_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