Blame libarchive/archive_write_add_filter_uuencode.c

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