archive_write_set_compression_none.c revision 267654
198184Sgordon/*-
298184Sgordon * Copyright (c) 2003-2007 Tim Kientzle
398184Sgordon * All rights reserved.
498184Sgordon *
598184Sgordon * Redistribution and use in source and binary forms, with or without
698184Sgordon * modification, are permitted provided that the following conditions
7197139Shrs * are met:
8179566Sbrooks * 1. Redistributions of source code must retain the above copyright
998184Sgordon *    notice, this list of conditions and the following disclaimer.
10104985Sschweikh * 2. Redistributions in binary form must reproduce the above copyright
1198184Sgordon *    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: releng/9.3/contrib/libarchive/libarchive/archive_write_set_compression_none.c 229592 2012-01-05 12:06:54Z mm $");
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_write_private.h"
42
43static int	archive_compressor_none_finish(struct archive_write *a);
44static int	archive_compressor_none_init(struct archive_write *);
45static int	archive_compressor_none_write(struct archive_write *,
46		    const void *, size_t);
47
48struct archive_none {
49	char	*buffer;
50	ssize_t	 buffer_size;
51	char	*next;		/* Current insert location */
52	ssize_t	 avail;		/* Free space left in buffer */
53};
54
55int
56archive_write_set_compression_none(struct archive *_a)
57{
58	struct archive_write *a = (struct archive_write *)_a;
59	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
60	    ARCHIVE_STATE_NEW, "archive_write_set_compression_none");
61	a->compressor.init = &archive_compressor_none_init;
62	return (0);
63}
64
65/*
66 * Setup callback.
67 */
68static int
69archive_compressor_none_init(struct archive_write *a)
70{
71	int ret;
72	struct archive_none *state;
73
74	a->archive.compression_code = ARCHIVE_COMPRESSION_NONE;
75	a->archive.compression_name = "none";
76
77	if (a->client_opener != NULL) {
78		ret = (a->client_opener)(&a->archive, a->client_data);
79		if (ret != 0)
80			return (ret);
81	}
82
83	state = (struct archive_none *)malloc(sizeof(*state));
84	if (state == NULL) {
85		archive_set_error(&a->archive, ENOMEM,
86		    "Can't allocate data for output buffering");
87		return (ARCHIVE_FATAL);
88	}
89	memset(state, 0, sizeof(*state));
90
91	state->buffer_size = a->bytes_per_block;
92	if (state->buffer_size != 0) {
93		state->buffer = (char *)malloc(state->buffer_size);
94		if (state->buffer == NULL) {
95			archive_set_error(&a->archive, ENOMEM,
96			    "Can't allocate output buffer");
97			free(state);
98			return (ARCHIVE_FATAL);
99		}
100	}
101
102	state->next = state->buffer;
103	state->avail = state->buffer_size;
104
105	a->compressor.data = state;
106	a->compressor.write = archive_compressor_none_write;
107	a->compressor.finish = archive_compressor_none_finish;
108	return (ARCHIVE_OK);
109}
110
111/*
112 * Write data to the stream.
113 */
114static int
115archive_compressor_none_write(struct archive_write *a, const void *vbuff,
116    size_t length)
117{
118	const char *buff;
119	ssize_t remaining, to_copy;
120	ssize_t bytes_written;
121	struct archive_none *state;
122
123	state = (struct archive_none *)a->compressor.data;
124	buff = (const char *)vbuff;
125	if (a->client_writer == NULL) {
126		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
127		    "No write callback is registered?  "
128		    "This is probably an internal programming error.");
129		return (ARCHIVE_FATAL);
130	}
131
132	remaining = length;
133
134	/*
135	 * If there is no buffer for blocking, just pass the data
136	 * straight through to the client write callback.  In
137	 * particular, this supports "no write delay" operation for
138	 * special applications.  Just set the block size to zero.
139	 */
140	if (state->buffer_size == 0) {
141		while (remaining > 0) {
142			bytes_written = (a->client_writer)(&a->archive,
143			    a->client_data, buff, remaining);
144			if (bytes_written <= 0)
145				return (ARCHIVE_FATAL);
146			a->archive.raw_position += bytes_written;
147			remaining -= bytes_written;
148			buff += bytes_written;
149		}
150		a->archive.file_position += length;
151		return (ARCHIVE_OK);
152	}
153
154	/* If the copy buffer isn't empty, try to fill it. */
155	if (state->avail < state->buffer_size) {
156		/* If buffer is not empty... */
157		/* ... copy data into buffer ... */
158		to_copy = (remaining > state->avail) ?
159		    state->avail : remaining;
160		memcpy(state->next, buff, to_copy);
161		state->next += to_copy;
162		state->avail -= to_copy;
163		buff += to_copy;
164		remaining -= to_copy;
165		/* ... if it's full, write it out. */
166		if (state->avail == 0) {
167			bytes_written = (a->client_writer)(&a->archive,
168			    a->client_data, state->buffer, state->buffer_size);
169			if (bytes_written <= 0)
170				return (ARCHIVE_FATAL);
171			/* XXX TODO: if bytes_written < state->buffer_size */
172			a->archive.raw_position += bytes_written;
173			state->next = state->buffer;
174			state->avail = state->buffer_size;
175		}
176	}
177
178	while (remaining > state->buffer_size) {
179		/* Write out full blocks directly to client. */
180		bytes_written = (a->client_writer)(&a->archive,
181		    a->client_data, buff, state->buffer_size);
182		if (bytes_written <= 0)
183			return (ARCHIVE_FATAL);
184		a->archive.raw_position += bytes_written;
185		buff += bytes_written;
186		remaining -= bytes_written;
187	}
188
189	if (remaining > 0) {
190		/* Copy last bit into copy buffer. */
191		memcpy(state->next, buff, remaining);
192		state->next += remaining;
193		state->avail -= remaining;
194	}
195
196	a->archive.file_position += length;
197	return (ARCHIVE_OK);
198}
199
200
201/*
202 * Finish the compression.
203 */
204static int
205archive_compressor_none_finish(struct archive_write *a)
206{
207	ssize_t block_length;
208	ssize_t target_block_length;
209	ssize_t bytes_written;
210	int ret;
211	struct archive_none *state;
212
213	state = (struct archive_none *)a->compressor.data;
214	ret = ARCHIVE_OK;
215	if (a->client_writer == NULL) {
216		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
217		    "No write callback is registered?  "
218		    "This is probably an internal programming error.");
219		return (ARCHIVE_FATAL);
220	}
221
222	/* If there's pending data, pad and write the last block */
223	if (state->next != state->buffer) {
224		block_length = state->buffer_size - state->avail;
225
226		/* Tricky calculation to determine size of last block */
227		if (a->bytes_in_last_block <= 0)
228			/* Default or Zero: pad to full block */
229			target_block_length = a->bytes_per_block;
230		else
231			/* Round to next multiple of bytes_in_last_block. */
232			target_block_length = a->bytes_in_last_block *
233			    ( (block_length + a->bytes_in_last_block - 1) /
234				a->bytes_in_last_block);
235		if (target_block_length > a->bytes_per_block)
236			target_block_length = a->bytes_per_block;
237		if (block_length < target_block_length) {
238			memset(state->next, 0,
239			    target_block_length - block_length);
240			block_length = target_block_length;
241		}
242		bytes_written = (a->client_writer)(&a->archive,
243		    a->client_data, state->buffer, block_length);
244		if (bytes_written <= 0)
245			ret = ARCHIVE_FATAL;
246		else {
247			a->archive.raw_position += bytes_written;
248			ret = ARCHIVE_OK;
249		}
250	}
251	if (state->buffer)
252		free(state->buffer);
253	free(state);
254	a->compressor.data = NULL;
255
256	return (ret);
257}
258