]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_write_add_filter_uuencode.c
1 /*-
2  * Copyright (c) 2012 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
28 __FBSDID("$FreeBSD$");
29
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39
40 #include "archive.h"
41 #include "archive_private.h"
42 #include "archive_string.h"
43 #include "archive_write_private.h"
44
45 #define LBYTES 45
46
47 struct private_uuencode {
48         int                     mode;
49         struct archive_string   name;
50         struct archive_string   encoded_buff;
51         size_t                  bs;
52         size_t                  hold_len;
53         unsigned char           hold[LBYTES];
54 };
55
56 static int archive_filter_uuencode_options(struct archive_write_filter *,
57     const char *, const char *);
58 static int archive_filter_uuencode_open(struct archive_write_filter *);
59 static int archive_filter_uuencode_write(struct archive_write_filter *,
60     const void *, size_t);
61 static int archive_filter_uuencode_close(struct archive_write_filter *);
62 static int archive_filter_uuencode_free(struct archive_write_filter *);
63 static void uu_encode(struct archive_string *, const unsigned char *, size_t);
64 static int64_t atol8(const char *, size_t);
65
66 /*
67  * Add a compress filter to this write handle.
68  */
69 int
70 archive_write_add_filter_uuencode(struct archive *_a)
71 {
72         struct archive_write *a = (struct archive_write *)_a;
73         struct archive_write_filter *f = __archive_write_allocate_filter(_a);
74         struct private_uuencode *state;
75
76         archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
77             ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
78
79         state = (struct private_uuencode *)calloc(1, sizeof(*state));
80         if (state == NULL) {
81                 archive_set_error(f->archive, ENOMEM,
82                     "Can't allocate data for uuencode filter");
83                 return (ARCHIVE_FATAL);
84         }
85         archive_strcpy(&state->name, "-");
86         state->mode = 0644;
87
88         f->data = state;
89         f->name = "uuencode";
90         f->code = ARCHIVE_FILTER_UU;
91         f->open = archive_filter_uuencode_open;
92         f->options = archive_filter_uuencode_options;
93         f->write = archive_filter_uuencode_write;
94         f->close = archive_filter_uuencode_close;
95         f->free = archive_filter_uuencode_free;
96
97         return (ARCHIVE_OK);
98 }
99
100 /*
101  * Set write options.
102  */
103 static int
104 archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
105     const char *value)
106 {
107         struct private_uuencode *state = (struct private_uuencode *)f->data;
108
109         if (strcmp(key, "mode") == 0) {
110                 if (value == NULL) {
111                         archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
112                             "mode option requires octal digits");
113                         return (ARCHIVE_FAILED);
114                 }
115                 state->mode = (int)atol8(value, strlen(value)) & 0777;
116                 return (ARCHIVE_OK);
117         } else if (strcmp(key, "name") == 0) {
118                 if (value == NULL) {
119                         archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
120                             "name option requires a string");
121                         return (ARCHIVE_FAILED);
122                 }
123                 archive_strcpy(&state->name, value);
124                 return (ARCHIVE_OK);
125         }
126
127         /* Note: The "warn" return is just to inform the options
128          * supervisor that we didn't handle it.  It will generate
129          * a suitable error if no one used this option. */
130         return (ARCHIVE_WARN);
131 }
132
133 /*
134  * Setup callback.
135  */
136 static int
137 archive_filter_uuencode_open(struct archive_write_filter *f)
138 {
139         struct private_uuencode *state = (struct private_uuencode *)f->data;
140         size_t bs = 65536, bpb;
141
142         if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
143                 /* Buffer size should be a multiple number of the of bytes
144                  * per block for performance. */
145                 bpb = archive_write_get_bytes_per_block(f->archive);
146                 if (bpb > bs)
147                         bs = bpb;
148                 else if (bpb != 0)
149                         bs -= bs % bpb;
150         }
151
152         state->bs = bs;
153         if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
154                 archive_set_error(f->archive, ENOMEM,
155                     "Can't allocate data for uuencode buffer");
156                 return (ARCHIVE_FATAL);
157         }
158
159         archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
160             state->mode, state->name.s);
161
162         f->data = state;
163         return (0);
164 }
165
166 static void
167 uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
168 {
169         int c;
170
171         c = (int)len;
172         archive_strappend_char(as, c?c + 0x20:'`');
173         for (; len >= 3; p += 3, len -= 3) {
174                 c = p[0] >> 2;
175                 archive_strappend_char(as, c?c + 0x20:'`');
176                 c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
177                 archive_strappend_char(as, c?c + 0x20:'`');
178                 c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
179                 archive_strappend_char(as, c?c + 0x20:'`');
180                 c = p[2] & 0x3f;
181                 archive_strappend_char(as, c?c + 0x20:'`');
182         }
183         if (len > 0) {
184                 c = p[0] >> 2;
185                 archive_strappend_char(as, c?c + 0x20:'`');
186                 c = (p[0] & 0x03) << 4;
187                 if (len == 1) {
188                         archive_strappend_char(as, c?c + 0x20:'`');
189                         archive_strappend_char(as, '`');
190                         archive_strappend_char(as, '`');
191                 } else {
192                         c |= (p[1] & 0xf0) >> 4;
193                         archive_strappend_char(as, c?c + 0x20:'`');
194                         c = (p[1] & 0x0f) << 2;
195                         archive_strappend_char(as, c?c + 0x20:'`');
196                         archive_strappend_char(as, '`');
197                 }
198         }
199         archive_strappend_char(as, '\n');
200 }
201
202 /*
203  * Write data to the encoded stream.
204  */
205 static int
206 archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
207     size_t length)
208 {
209         struct private_uuencode *state = (struct private_uuencode *)f->data;
210         const unsigned char *p = buff;
211         int ret = ARCHIVE_OK;
212
213         if (length == 0)
214                 return (ret);
215
216         if (state->hold_len) {
217                 while (state->hold_len < LBYTES && length > 0) {
218                         state->hold[state->hold_len++] = *p++;
219                         length--;
220                 }
221                 if (state->hold_len < LBYTES)
222                         return (ret);
223                 uu_encode(&state->encoded_buff, state->hold, LBYTES);
224                 state->hold_len = 0;
225         }
226
227         for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
228                 uu_encode(&state->encoded_buff, p, LBYTES);
229
230         /* Save remaining bytes. */
231         if (length > 0) {
232                 memcpy(state->hold, p, length);
233                 state->hold_len = length;
234         }
235         while (archive_strlen(&state->encoded_buff) >= state->bs) {
236                 ret = __archive_write_filter(f->next_filter,
237                     state->encoded_buff.s, state->bs);
238                 memmove(state->encoded_buff.s,
239                     state->encoded_buff.s + state->bs,
240                     state->encoded_buff.length - state->bs);
241                 state->encoded_buff.length -= state->bs;
242         }
243
244         return (ret);
245 }
246
247
248 /*
249  * Finish the compression...
250  */
251 static int
252 archive_filter_uuencode_close(struct archive_write_filter *f)
253 {
254         struct private_uuencode *state = (struct private_uuencode *)f->data;
255
256         /* Flush remaining bytes. */
257         if (state->hold_len != 0)
258                 uu_encode(&state->encoded_buff, state->hold, state->hold_len);
259         archive_string_sprintf(&state->encoded_buff, "`\nend\n");
260         /* Write the last block */
261         archive_write_set_bytes_in_last_block(f->archive, 1);
262         return __archive_write_filter(f->next_filter,
263             state->encoded_buff.s, archive_strlen(&state->encoded_buff));
264 }
265
266 static int
267 archive_filter_uuencode_free(struct archive_write_filter *f)
268 {
269         struct private_uuencode *state = (struct private_uuencode *)f->data;
270
271         archive_string_free(&state->name);
272         archive_string_free(&state->encoded_buff);
273         free(state);
274         return (ARCHIVE_OK);
275 }
276
277 static int64_t
278 atol8(const char *p, size_t char_cnt)
279 {
280         int64_t l;
281         int digit;
282         
283         l = 0;
284         while (char_cnt-- > 0) {
285                 if (*p >= '0' && *p <= '7')
286                         digit = *p - '0';
287                 else
288                         break;
289                 p++;
290                 l <<= 3;
291                 l |= digit;
292         }
293         return (l);
294 }
295