summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@FreeBSD.org>2006-11-24 02:00:48 +0000
committerTim Kientzle <kientzle@FreeBSD.org>2006-11-24 02:00:48 +0000
commit13e2d5bcd5d782ffbb0b00cfd56bc1a0c0d438fa (patch)
tree1718edba490b96d4478714d3358a3b2dfc19fda7
parentac39496f20a3ad81373c2f442303af3d571d560c (diff)
Notes
-rw-r--r--lib/libarchive/archive.h.in16
-rw-r--r--lib/libarchive/archive_read_open_file.c177
-rw-r--r--lib/libarchive/archive_read_open_memory.c147
-rw-r--r--lib/libarchive/archive_write_open_file.c98
-rw-r--r--lib/libarchive/archive_write_open_memory.c127
5 files changed, 354 insertions, 211 deletions
diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in
index 1deced1ad1c67..4567de7099e58 100644
--- a/lib/libarchive/archive.h.in
+++ b/lib/libarchive/archive.h.in
@@ -37,6 +37,7 @@
#include <sys/types.h> /* Linux requires this for off_t */
@ARCHIVE_H_INCLUDE_INTTYPES_H@
+#include <stdio.h> /* For FILE * */
#include <unistd.h> /* For ssize_t and size_t */
#ifdef __cplusplus
@@ -58,6 +59,7 @@ extern "C" {
*
* 1 - Version tests are available.
* 2 - archive_{read,write}_close available separately from _finish.
+ * 3 - open_memory, open_memory2, open_FILE, open_fd available
*/
#define ARCHIVE_API_VERSION @ARCHIVE_API_MAJOR@
int archive_api_version(void);
@@ -206,9 +208,18 @@ int archive_read_open_filename(struct archive *,
/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
int archive_read_open_file(struct archive *,
const char *_filename, size_t _block_size);
+/* Read an archive that's stored in memory. */
+int archive_read_open_memory(struct archive *,
+ void * buff, size_t size);
+/* A more involved version that is only used for internal testing. */
+int archive_read_open_memory2(struct archive *a, void *buff,
+ size_t size, size_t read_size);
/* Read an archive that's already open, using the file descriptor. */
int archive_read_open_fd(struct archive *, int _fd,
size_t _block_size);
+/* Read an archive that's already open, using a FILE *. */
+/* Note: DO NOT use this with tape drives. */
+int archive_read_open_FILE(struct archive *, FILE *_file);
/* Parses and returns next entry header. */
int archive_read_next_header(struct archive *,
@@ -333,6 +344,11 @@ int archive_write_open_fd(struct archive *, int _fd);
int archive_write_open_filename(struct archive *, const char *_file);
/* A deprecated synonym for archive_write_open_filename() */
int archive_write_open_file(struct archive *, const char *_file);
+int archive_write_open_FILE(struct archive *, FILE *);
+/* _buffSize is the size of the buffer, _used refers to a variable that
+ * will be updated after each write into the buffer. */
+int archive_write_open_memory(struct archive *,
+ void *_buffer, size_t _buffSize, size_t *_used);
/*
* Note that the library will truncate writes beyond the size provided
diff --git a/lib/libarchive/archive_read_open_file.c b/lib/libarchive/archive_read_open_file.c
index cefc2861b13ef..cb18fc3ad9e13 100644
--- a/lib/libarchive/archive_read_open_file.c
+++ b/lib/libarchive/archive_read_open_file.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2004 Tim Kientzle
+ * Copyright (c) 2003-2006 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -48,12 +48,10 @@ __FBSDID("$FreeBSD$");
#include "archive.h"
-struct read_file_data {
- int fd;
+struct read_FILE_data {
+ FILE *f;
size_t block_size;
void *buffer;
- mode_t st_mode; /* Mode bits for opened file. */
- char filename[1]; /* Must be last! */
};
static int file_close(struct archive *, void *);
@@ -62,91 +60,54 @@ static ssize_t file_read(struct archive *, void *, const void **buff);
static ssize_t file_skip(struct archive *, void *, size_t request);
int
-archive_read_open_file(struct archive *a, const char *filename,
- size_t block_size)
+archive_read_open_FILE(struct archive *a, FILE *f)
{
- return (archive_read_open_filename(a, filename, block_size));
-}
+ struct read_FILE_data *mine;
-int
-archive_read_open_filename(struct archive *a, const char *filename,
- size_t block_size)
-{
- struct read_file_data *mine;
-
- if (filename == NULL || filename[0] == '\0') {
- mine = (struct read_file_data *)malloc(sizeof(*mine));
- if (mine == NULL) {
- archive_set_error(a, ENOMEM, "No memory");
- return (ARCHIVE_FATAL);
- }
- mine->filename[0] = '\0';
- } else {
- mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename));
- if (mine == NULL) {
- archive_set_error(a, ENOMEM, "No memory");
- return (ARCHIVE_FATAL);
- }
- strcpy(mine->filename, filename);
+ mine = (struct read_FILE_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
}
- mine->block_size = block_size;
- mine->buffer = NULL;
- mine->fd = -1;
- return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close));
+ mine->block_size = 128 * 1024;
+ mine->buffer = malloc(mine->block_size);
+ if (mine->buffer == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ free(mine);
+ return (ARCHIVE_FATAL);
+ }
+ mine->f = f;
+ return (archive_read_open2(a, mine, file_open, file_read,
+ file_skip, file_close));
}
static int
file_open(struct archive *a, void *client_data)
{
- struct read_file_data *mine = (struct read_file_data *)client_data;
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
struct stat st;
- mine->buffer = malloc(mine->block_size);
- if (mine->buffer == NULL) {
- archive_set_error(a, ENOMEM, "No memory");
- return (ARCHIVE_FATAL);
- }
- if (mine->filename[0] != '\0')
- mine->fd = open(mine->filename, O_RDONLY);
- else
- mine->fd = 0; /* Fake "open" for stdin. */
- if (mine->fd < 0) {
- archive_set_error(a, errno, "Failed to open '%s'",
- mine->filename);
- return (ARCHIVE_FATAL);
- }
- if (fstat(mine->fd, &st) == 0) {
- /* If we're reading a file from disk, ensure that we don't
- overwrite it with an extracted file. */
- if (S_ISREG(st.st_mode))
- archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
- /* Remember mode so close can decide whether to flush. */
- mine->st_mode = st.st_mode;
- } else {
- if (mine->filename[0] == '\0')
- archive_set_error(a, errno, "Can't stat stdin");
- else
- archive_set_error(a, errno, "Can't stat '%s'",
- mine->filename);
- return (ARCHIVE_FATAL);
- }
- return (0);
+ /*
+ * If we can't fstat() the file, it may just be that
+ * it's not a file. (FILE * objects can wrap many kinds
+ * of I/O streams.)
+ */
+ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode))
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+
+ return (ARCHIVE_OK);
}
static ssize_t
file_read(struct archive *a, void *client_data, const void **buff)
{
- struct read_file_data *mine = (struct read_file_data *)client_data;
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
ssize_t bytes_read;
*buff = mine->buffer;
- bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+ bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
if (bytes_read < 0) {
- if (mine->filename[0] == '\0')
- archive_set_error(a, errno, "Error reading stdin");
- else
- archive_set_error(a, errno, "Error reading '%s'",
- mine->filename);
+ archive_set_error(a, errno, "Error reading file");
}
return (bytes_read);
}
@@ -154,77 +115,33 @@ file_read(struct archive *a, void *client_data, const void **buff)
static ssize_t
file_skip(struct archive *a, void *client_data, size_t request)
{
- struct read_file_data *mine = (struct read_file_data *)client_data;
- off_t old_offset, new_offset;
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
- /* Reduce request to the next smallest multiple of block_size */
- request = (request / mine->block_size) * mine->block_size;
/*
- * Hurray for lazy evaluation: if the first lseek fails, the second
- * one will not be executed.
+ * Note: the 'fd' and 'filename' versions round the request
+ * down to a multiple of the block size to ensure proper
+ * operation on block-oriented media such as tapes. But stdio
+ * doesn't work with such media (it doesn't ensure blocking),
+ * so we don't need to bother.
*/
- if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
- ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
+#if HAVE_FSEEKO
+ if (fseeko(mine->f, request, SEEK_CUR) != 0)
+#else
+ if (fseek(mine->f, request, SEEK_CUR) != 0)
+#endif
{
- if (errno == ESPIPE)
- {
- /*
- * Failure to lseek() can be caused by the file
- * descriptor pointing to a pipe, socket or FIFO.
- * Return 0 here, so the compression layer will use
- * read()s instead to advance the file descriptor.
- * It's slower of course, but works as well.
- */
- return (0);
- }
- /*
- * There's been an error other than ESPIPE. This is most
- * likely caused by a programmer error (too large request)
- * or a corrupted archive file.
- */
- if (mine->filename[0] == '\0')
- /*
- * Should never get here, since lseek() on stdin ought
- * to return an ESPIPE error.
- */
- archive_set_error(a, errno, "Error seeking in stdin");
- else
- archive_set_error(a, errno, "Error seeking in '%s'",
- mine->filename);
- return (-1);
+ archive_set_error(a, errno, "Error skipping forward");
+ return (ARCHIVE_FATAL);
}
- return (new_offset - old_offset);
+ return (request);
}
static int
file_close(struct archive *a, void *client_data)
{
- struct read_file_data *mine = (struct read_file_data *)client_data;
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
(void)a; /* UNUSED */
-
- /*
- * Sometimes, we should flush the input before closing.
- * Regular files: faster to just close without flush.
- * Devices: must not flush (user might need to
- * read the "next" item on a non-rewind device).
- * Pipes and sockets: must flush (otherwise, the
- * program feeding the pipe or socket may complain).
- * Here, I flush everything except for regular files and
- * device nodes.
- */
- if (!S_ISREG(mine->st_mode)
- && !S_ISCHR(mine->st_mode)
- && !S_ISBLK(mine->st_mode)) {
- ssize_t bytesRead;
- do {
- bytesRead = read(mine->fd, mine->buffer,
- mine->block_size);
- } while (bytesRead > 0);
- }
- /* If a named file was opened, then it needs to be closed. */
- if (mine->filename[0] != '\0')
- close(mine->fd);
if (mine->buffer != NULL)
free(mine->buffer);
free(mine);
diff --git a/lib/libarchive/archive_read_open_memory.c b/lib/libarchive/archive_read_open_memory.c
new file mode 100644
index 0000000000000..a639d85de6557
--- /dev/null
+++ b/lib/libarchive/archive_read_open_memory.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2003-2006 Tim Kientzle
+ * 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$");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+/*
+ * Glue to read an archive from a block of memory.
+ *
+ * This is mostly a huge help in building test harnesses;
+ * test programs can build archives in memory and read them
+ * back again without having to mess with files on disk.
+ */
+
+struct read_memory_data {
+ unsigned char *buffer;
+ unsigned char *end;
+ ssize_t read_size;
+};
+
+static int memory_read_close(struct archive *, void *);
+static int memory_read_open(struct archive *, void *);
+static ssize_t memory_read_skip(struct archive *, void *, size_t request);
+static ssize_t memory_read(struct archive *, void *, const void **buff);
+
+int
+archive_read_open_memory(struct archive *a, void *buff, size_t size)
+{
+ return archive_read_open_memory2(a, buff, size, size);
+}
+
+/*
+ * Don't use _open_memory2() in production code; the archive_read_open_memory()
+ * version is the one you really want. This is just here so that
+ * test harnesses can exercise block operations inside the library.
+ */
+int
+archive_read_open_memory2(struct archive *a, void *buff,
+ size_t size, size_t read_size)
+{
+ struct read_memory_data *mine;
+
+ mine = (struct read_memory_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ memset(mine, 0, sizeof(*mine));
+ mine->buffer = (unsigned char *)buff;
+ mine->end = mine->buffer + size;
+ mine->read_size = read_size;
+ return (archive_read_open2(a, mine, memory_read_open,
+ memory_read, memory_read_skip, memory_read_close));
+}
+
+/*
+ * There's nothing to open.
+ */
+static int
+memory_read_open(struct archive *a, void *client_data)
+{
+ (void)a; /* UNUSED */
+ (void)client_data; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This is scary simple: Just advance a pointer. Limiting
+ * to read_size is not technically necessary, but it exercises
+ * more of the internal logic when used with a small block size
+ * in a test harness. Production use should not specify a block
+ * size; then this is much faster.
+ */
+static ssize_t
+memory_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+ ssize_t size;
+
+ (void)a; /* UNUSED */
+ *buff = mine->buffer;
+ size = mine->end - mine->buffer;
+ if (size > mine->read_size)
+ size = mine->read_size;
+ mine->buffer += size;
+ return (size);
+}
+
+/*
+ * Advancing is just as simple. Again, this is doing more than
+ * necessary in order to better exercise internal code when used
+ * as a test harness.
+ */
+static ssize_t
+memory_read_skip(struct archive *a, void *client_data, size_t skip)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+ (void)a; /* UNUSED */
+ if (mine->buffer + skip > mine->end)
+ skip = mine->end - mine->buffer;
+ /* Round down to block size. */
+ skip /= mine->read_size;
+ skip *= mine->read_size;
+ mine->buffer += skip;
+ return (skip);
+}
+
+/*
+ * Close is just cleaning up our one small bit of data.
+ */
+static int
+memory_read_close(struct archive *a, void *client_data)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+ (void)a; /* UNUSED */
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/lib/libarchive/archive_write_open_file.c b/lib/libarchive/archive_write_open_file.c
index c37caeb2fa31f..0eb1a0320445d 100644
--- a/lib/libarchive/archive_write_open_file.c
+++ b/lib/libarchive/archive_write_open_file.c
@@ -48,9 +48,8 @@ __FBSDID("$FreeBSD$");
#include "archive.h"
-struct write_file_data {
- int fd;
- char filename[1];
+struct write_FILE_data {
+ FILE *f;
};
static int file_close(struct archive *, void *);
@@ -58,32 +57,16 @@ static int file_open(struct archive *, void *);
static ssize_t file_write(struct archive *, void *, void *buff, size_t);
int
-archive_write_open_file(struct archive *a, const char *filename)
+archive_write_open_FILE(struct archive *a, FILE *f)
{
- return (archive_write_open_filename(a, filename));
-}
-
-int
-archive_write_open_filename(struct archive *a, const char *filename)
-{
- struct write_file_data *mine;
+ struct write_FILE_data *mine;
- if (filename == NULL || filename[0] == '\0') {
- mine = (struct write_file_data *)malloc(sizeof(*mine));
- if (mine == NULL) {
- archive_set_error(a, ENOMEM, "No memory");
- return (ARCHIVE_FATAL);
- }
- mine->filename[0] = '\0'; /* Record that we're using stdout. */
- } else {
- mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename));
- if (mine == NULL) {
- archive_set_error(a, ENOMEM, "No memory");
- return (ARCHIVE_FATAL);
- }
- strcpy(mine->filename, filename);
+ mine = (struct write_FILE_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
}
- mine->fd = -1;
+ mine->f = f;
return (archive_write_open(a, mine,
file_open, file_write, file_close));
}
@@ -91,53 +74,8 @@ archive_write_open_filename(struct archive *a, const char *filename)
static int
file_open(struct archive *a, void *client_data)
{
- int flags;
- struct write_file_data *mine;
- struct stat st;
-
- mine = (struct write_file_data *)client_data;
- flags = O_WRONLY | O_CREAT | O_TRUNC;
-
- /*
- * Open the file.
- */
- if (mine->filename[0] != '\0') {
- mine->fd = open(mine->filename, flags, 0666);
- if (mine->fd < 0) {
- archive_set_error(a, errno, "Failed to open '%s'",
- mine->filename);
- return (ARCHIVE_FATAL);
- }
- } else {
- /*
- * NULL filename is stdout.
- */
- mine->fd = 1;
- /* By default, pad archive when writing to stdout. */
- if (archive_write_get_bytes_in_last_block(a) < 0)
- archive_write_set_bytes_in_last_block(a, 0);
- }
-
- /*
- * Set up default last block handling.
- */
- if (archive_write_get_bytes_in_last_block(a) < 0) {
- if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
- S_ISFIFO(st.st_mode))
- /* Pad last block when writing to device or FIFO. */
- archive_write_set_bytes_in_last_block(a, 0);
- else
- /* Don't pad last block otherwise. */
- archive_write_set_bytes_in_last_block(a, 1);
- }
-
- /*
- * If the output file is a regular file, don't add it to
- * itself. If it's a device file, it's okay to add the device
- * entry to the output archive.
- */
- if (S_ISREG(st.st_mode))
- archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+ (void)a; /* UNUSED */
+ (void)client_data; /* UNUSED */
return (ARCHIVE_OK);
}
@@ -145,12 +83,12 @@ file_open(struct archive *a, void *client_data)
static ssize_t
file_write(struct archive *a, void *client_data, void *buff, size_t length)
{
- struct write_file_data *mine;
- ssize_t bytesWritten;
+ struct write_FILE_data *mine;
+ size_t bytesWritten;
- mine = (struct write_file_data *)client_data;
- bytesWritten = write(mine->fd, buff, length);
- if (bytesWritten <= 0) {
+ mine = client_data;
+ bytesWritten = fwrite(buff, 1, length, mine->f);
+ if (bytesWritten < length) {
archive_set_error(a, errno, "Write error");
return (-1);
}
@@ -160,11 +98,9 @@ file_write(struct archive *a, void *client_data, void *buff, size_t length)
static int
file_close(struct archive *a, void *client_data)
{
- struct write_file_data *mine = (struct write_file_data *)client_data;
+ struct write_FILE_data *mine = client_data;
(void)a; /* UNUSED */
- if (mine->filename[0] != '\0')
- close(mine->fd);
free(mine);
return (ARCHIVE_OK);
}
diff --git a/lib/libarchive/archive_write_open_memory.c b/lib/libarchive/archive_write_open_memory.c
new file mode 100644
index 0000000000000..58baebdbed573
--- /dev/null
+++ b/lib/libarchive/archive_write_open_memory.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2003-2006 Tim Kientzle
+ * 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
+ * in this position and unchanged.
+ * 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$");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+/*
+ * This is a little tricky. I used to allow the
+ * compression handling layer to fork the compressor,
+ * which means this write function gets invoked in
+ * a separate process. That would, of course, make it impossible
+ * to actually use the data stored into memory here.
+ * Fortunately, none of the compressors fork today and
+ * I'm reluctant to use that route in the future but, if
+ * forking compressors ever do reappear, this will have
+ * to get a lot more complicated.
+ */
+
+struct write_memory_data {
+ size_t used;
+ size_t size;
+ size_t * client_size;
+ unsigned char * buff;
+};
+
+static int memory_write_close(struct archive *, void *);
+static int memory_write_open(struct archive *, void *);
+static ssize_t memory_write(struct archive *, void *, void *buff, size_t);
+
+/*
+ * Client provides a pointer to a block of memory to receive
+ * the data. The 'size' param both tells us the size of the
+ * client buffer and lets us tell the client the final size.
+ */
+int
+archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used)
+{
+ struct write_memory_data *mine;
+
+ mine = (struct write_memory_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ memset(mine, 0, sizeof(*mine));
+ mine->buff = buff;
+ mine->size = buffSize;
+ mine->client_size = used;
+ return (archive_write_open(a, mine,
+ memory_write_open, memory_write, memory_write_close));
+}
+
+static int
+memory_write_open(struct archive *a, void *client_data)
+{
+ struct write_memory_data *mine;
+ mine = client_data;
+ mine->used = 0;
+ if (mine->client_size != NULL)
+ *mine->client_size = mine->used;
+ /* Disable padding if it hasn't been set explicitly. */
+ if (-1 == archive_write_get_bytes_in_last_block(a))
+ archive_write_set_bytes_in_last_block(a, 1);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Copy the data into the client buffer.
+ * Note that we update mine->client_size on every write.
+ * In particular, this means the client can follow exactly
+ * how much has been written into their buffer at any time.
+ */
+static ssize_t
+memory_write(struct archive *a, void *client_data, void *buff, size_t length)
+{
+ struct write_memory_data *mine;
+ mine = client_data;
+
+ if (mine->used + length > mine->size) {
+ archive_set_error(a, ENOMEM, "Buffer exhausted");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(mine->buff + mine->used, buff, length);
+ mine->used += length;
+ if (mine->client_size != NULL)
+ *mine->client_size = mine->used;
+ return (length);
+}
+
+static int
+memory_write_close(struct archive *a, void *client_data)
+{
+ struct write_memory_data *mine;
+ (void)a; /* UNUSED */
+ mine = client_data;
+ free(mine);
+ return (ARCHIVE_OK);
+}