archive_write_add_filter_uuencode.c revision 302408
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
47struct 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
56static int archive_filter_uuencode_options(struct archive_write_filter *,
57    const char *, const char *);
58static int archive_filter_uuencode_open(struct archive_write_filter *);
59static int archive_filter_uuencode_write(struct archive_write_filter *,
60    const void *, size_t);
61static int archive_filter_uuencode_close(struct archive_write_filter *);
62static int archive_filter_uuencode_free(struct archive_write_filter *);
63static void uu_encode(struct archive_string *, const unsigned char *, size_t);
64static int64_t atol8(const char *, size_t);
65
66/*
67 * Add a compress filter to this write handle.
68 */
69int
70archive_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 */
103static int
104archive_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 */
136static int
137archive_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	int ret;
142
143	ret = __archive_write_open_filter(f->next_filter);
144	if (ret != ARCHIVE_OK)
145		return (ret);
146
147	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
148		/* Buffer size should be a multiple number of the of bytes
149		 * per block for performance. */
150		bpb = archive_write_get_bytes_per_block(f->archive);
151		if (bpb > bs)
152			bs = bpb;
153		else if (bpb != 0)
154			bs -= bs % bpb;
155	}
156
157	state->bs = bs;
158	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
159		archive_set_error(f->archive, ENOMEM,
160		    "Can't allocate data for uuencode buffer");
161		return (ARCHIVE_FATAL);
162	}
163
164	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
165	    state->mode, state->name.s);
166
167	f->data = state;
168	return (0);
169}
170
171static void
172uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
173{
174	int c;
175
176	c = (int)len;
177	archive_strappend_char(as, c?c + 0x20:'`');
178	for (; len >= 3; p += 3, len -= 3) {
179		c = p[0] >> 2;
180		archive_strappend_char(as, c?c + 0x20:'`');
181		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
182		archive_strappend_char(as, c?c + 0x20:'`');
183		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
184		archive_strappend_char(as, c?c + 0x20:'`');
185		c = p[2] & 0x3f;
186		archive_strappend_char(as, c?c + 0x20:'`');
187	}
188	if (len > 0) {
189		c = p[0] >> 2;
190		archive_strappend_char(as, c?c + 0x20:'`');
191		c = (p[0] & 0x03) << 4;
192		if (len == 1) {
193			archive_strappend_char(as, c?c + 0x20:'`');
194			archive_strappend_char(as, '`');
195			archive_strappend_char(as, '`');
196		} else {
197			c |= (p[1] & 0xf0) >> 4;
198			archive_strappend_char(as, c?c + 0x20:'`');
199			c = (p[1] & 0x0f) << 2;
200			archive_strappend_char(as, c?c + 0x20:'`');
201			archive_strappend_char(as, '`');
202		}
203	}
204	archive_strappend_char(as, '\n');
205}
206
207/*
208 * Write data to the encoded stream.
209 */
210static int
211archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
212    size_t length)
213{
214	struct private_uuencode *state = (struct private_uuencode *)f->data;
215	const unsigned char *p = buff;
216	int ret = ARCHIVE_OK;
217
218	if (length == 0)
219		return (ret);
220
221	if (state->hold_len) {
222		while (state->hold_len < LBYTES && length > 0) {
223			state->hold[state->hold_len++] = *p++;
224			length--;
225		}
226		if (state->hold_len < LBYTES)
227			return (ret);
228		uu_encode(&state->encoded_buff, state->hold, LBYTES);
229		state->hold_len = 0;
230	}
231
232	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
233		uu_encode(&state->encoded_buff, p, LBYTES);
234
235	/* Save remaining bytes. */
236	if (length > 0) {
237		memcpy(state->hold, p, length);
238		state->hold_len = length;
239	}
240	while (archive_strlen(&state->encoded_buff) >= state->bs) {
241		ret = __archive_write_filter(f->next_filter,
242		    state->encoded_buff.s, state->bs);
243		memmove(state->encoded_buff.s,
244		    state->encoded_buff.s + state->bs,
245		    state->encoded_buff.length - state->bs);
246		state->encoded_buff.length -= state->bs;
247	}
248
249	return (ret);
250}
251
252
253/*
254 * Finish the compression...
255 */
256static int
257archive_filter_uuencode_close(struct archive_write_filter *f)
258{
259	struct private_uuencode *state = (struct private_uuencode *)f->data;
260	int ret, ret2;
261
262	/* Flush remaining bytes. */
263	if (state->hold_len != 0)
264		uu_encode(&state->encoded_buff, state->hold, state->hold_len);
265	archive_string_sprintf(&state->encoded_buff, "`\nend\n");
266	/* Write the last block */
267	archive_write_set_bytes_in_last_block(f->archive, 1);
268	ret = __archive_write_filter(f->next_filter,
269	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
270	ret2 = __archive_write_close_filter(f->next_filter);
271	if (ret > ret2)
272		ret = ret2;
273	return (ret);
274}
275
276static int
277archive_filter_uuencode_free(struct archive_write_filter *f)
278{
279	struct private_uuencode *state = (struct private_uuencode *)f->data;
280
281	archive_string_free(&state->name);
282	archive_string_free(&state->encoded_buff);
283	free(state);
284	return (ARCHIVE_OK);
285}
286
287static int64_t
288atol8(const char *p, size_t char_cnt)
289{
290	int64_t l;
291	int digit;
292
293	l = 0;
294	while (char_cnt-- > 0) {
295		if (*p >= '0' && *p <= '7')
296			digit = *p - '0';
297		else
298			break;
299		p++;
300		l <<= 3;
301		l |= digit;
302	}
303	return (l);
304}
305
306