Permalink
Fetching contributors…
Cannot retrieve contributors at this time
306 lines (268 sloc) 8.31 KB
/*-
* Copyright (c) 2012 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "archive.h"
#include "archive_private.h"
#include "archive_string.h"
#include "archive_write_private.h"
#define LBYTES 45
struct private_uuencode {
int mode;
struct archive_string name;
struct archive_string encoded_buff;
size_t bs;
size_t hold_len;
unsigned char hold[LBYTES];
};
static int archive_filter_uuencode_options(struct archive_write_filter *,
const char *, const char *);
static int archive_filter_uuencode_open(struct archive_write_filter *);
static int archive_filter_uuencode_write(struct archive_write_filter *,
const void *, size_t);
static int archive_filter_uuencode_close(struct archive_write_filter *);
static int archive_filter_uuencode_free(struct archive_write_filter *);
static void uu_encode(struct archive_string *, const unsigned char *, size_t);
static int64_t atol8(const char *, size_t);
/*
* Add a compress filter to this write handle.
*/
int
archive_write_add_filter_uuencode(struct archive *_a)
{
struct archive_write *a = (struct archive_write *)_a;
struct archive_write_filter *f = __archive_write_allocate_filter(_a);
struct private_uuencode *state;
archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
state = (struct private_uuencode *)calloc(1, sizeof(*state));
if (state == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for uuencode filter");
return (ARCHIVE_FATAL);
}
archive_strcpy(&state->name, "-");
state->mode = 0644;
f->data = state;
f->name = "uuencode";
f->code = ARCHIVE_FILTER_UU;
f->open = archive_filter_uuencode_open;
f->options = archive_filter_uuencode_options;
f->write = archive_filter_uuencode_write;
f->close = archive_filter_uuencode_close;
f->free = archive_filter_uuencode_free;
return (ARCHIVE_OK);
}
/*
* Set write options.
*/
static int
archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
const char *value)
{
struct private_uuencode *state = (struct private_uuencode *)f->data;
if (strcmp(key, "mode") == 0) {
if (value == NULL) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"mode option requires octal digits");
return (ARCHIVE_FAILED);
}
state->mode = (int)atol8(value, strlen(value)) & 0777;
return (ARCHIVE_OK);
} else if (strcmp(key, "name") == 0) {
if (value == NULL) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"name option requires a string");
return (ARCHIVE_FAILED);
}
archive_strcpy(&state->name, value);
return (ARCHIVE_OK);
}
/* Note: The "warn" return is just to inform the options
* supervisor that we didn't handle it. It will generate
* a suitable error if no one used this option. */
return (ARCHIVE_WARN);
}
/*
* Setup callback.
*/
static int
archive_filter_uuencode_open(struct archive_write_filter *f)
{
struct private_uuencode *state = (struct private_uuencode *)f->data;
size_t bs = 65536, bpb;
int ret;
ret = __archive_write_open_filter(f->next_filter);
if (ret != ARCHIVE_OK)
return (ret);
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of the of bytes
* per block for performance. */
bpb = archive_write_get_bytes_per_block(f->archive);
if (bpb > bs)
bs = bpb;
else if (bpb != 0)
bs -= bs % bpb;
}
state->bs = bs;
if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for uuencode buffer");
return (ARCHIVE_FATAL);
}
archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
state->mode, state->name.s);
f->data = state;
return (0);
}
static void
uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
{
int c;
c = (int)len;
archive_strappend_char(as, c?c + 0x20:'`');
for (; len >= 3; p += 3, len -= 3) {
c = p[0] >> 2;
archive_strappend_char(as, c?c + 0x20:'`');
c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
archive_strappend_char(as, c?c + 0x20:'`');
c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
archive_strappend_char(as, c?c + 0x20:'`');
c = p[2] & 0x3f;
archive_strappend_char(as, c?c + 0x20:'`');
}
if (len > 0) {
c = p[0] >> 2;
archive_strappend_char(as, c?c + 0x20:'`');
c = (p[0] & 0x03) << 4;
if (len == 1) {
archive_strappend_char(as, c?c + 0x20:'`');
archive_strappend_char(as, '`');
archive_strappend_char(as, '`');
} else {
c |= (p[1] & 0xf0) >> 4;
archive_strappend_char(as, c?c + 0x20:'`');
c = (p[1] & 0x0f) << 2;
archive_strappend_char(as, c?c + 0x20:'`');
archive_strappend_char(as, '`');
}
}
archive_strappend_char(as, '\n');
}
/*
* Write data to the encoded stream.
*/
static int
archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
struct private_uuencode *state = (struct private_uuencode *)f->data;
const unsigned char *p = buff;
int ret = ARCHIVE_OK;
if (length == 0)
return (ret);
if (state->hold_len) {
while (state->hold_len < LBYTES && length > 0) {
state->hold[state->hold_len++] = *p++;
length--;
}
if (state->hold_len < LBYTES)
return (ret);
uu_encode(&state->encoded_buff, state->hold, LBYTES);
state->hold_len = 0;
}
for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
uu_encode(&state->encoded_buff, p, LBYTES);
/* Save remaining bytes. */
if (length > 0) {
memcpy(state->hold, p, length);
state->hold_len = length;
}
while (archive_strlen(&state->encoded_buff) >= state->bs) {
ret = __archive_write_filter(f->next_filter,
state->encoded_buff.s, state->bs);
memmove(state->encoded_buff.s,
state->encoded_buff.s + state->bs,
state->encoded_buff.length - state->bs);
state->encoded_buff.length -= state->bs;
}
return (ret);
}
/*
* Finish the compression...
*/
static int
archive_filter_uuencode_close(struct archive_write_filter *f)
{
struct private_uuencode *state = (struct private_uuencode *)f->data;
int ret, ret2;
/* Flush remaining bytes. */
if (state->hold_len != 0)
uu_encode(&state->encoded_buff, state->hold, state->hold_len);
archive_string_sprintf(&state->encoded_buff, "`\nend\n");
/* Write the last block */
archive_write_set_bytes_in_last_block(f->archive, 1);
ret = __archive_write_filter(f->next_filter,
state->encoded_buff.s, archive_strlen(&state->encoded_buff));
ret2 = __archive_write_close_filter(f->next_filter);
if (ret > ret2)
ret = ret2;
return (ret);
}
static int
archive_filter_uuencode_free(struct archive_write_filter *f)
{
struct private_uuencode *state = (struct private_uuencode *)f->data;
archive_string_free(&state->name);
archive_string_free(&state->encoded_buff);
free(state);
return (ARCHIVE_OK);
}
static int64_t
atol8(const char *p, size_t char_cnt)
{
int64_t l;
int digit;
l = 0;
while (char_cnt-- > 0) {
if (*p >= '0' && *p <= '7')
digit = *p - '0';
else
break;
p++;
l <<= 3;
l |= digit;
}
return (l);
}