gunzip.c 2.06 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* gunzip.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <zlib.h>

#include "apk_defines.h"
#include "apk_io.h"

struct apk_gzip_istream {
	struct apk_istream is;
	struct apk_bstream *bs;
	z_stream zs;
	int z_err;
Timo Teräs's avatar
Timo Teräs committed
25
	int autoclose;
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
};

static size_t gz_read(void *stream, void *ptr, size_t size)
{
	struct apk_gzip_istream *gis =
		container_of(stream, struct apk_gzip_istream, is);

	if (gis->z_err == Z_DATA_ERROR || gis->z_err == Z_ERRNO)
		return -1;
	if (gis->z_err == Z_STREAM_END)
		return 0;

	if (ptr == NULL)
		return apk_istream_skip(&gis->is, size);

	gis->zs.avail_out = size;
	gis->zs.next_out  = ptr;

	while (gis->zs.avail_out != 0 && gis->z_err == Z_OK) {
		if (gis->zs.avail_in == 0) {
			gis->zs.avail_in = gis->bs->read(gis->bs, (void **) &gis->zs.next_in);
			if (gis->zs.avail_in < 0) {
				gis->z_err = Z_DATA_ERROR;
				return size - gis->zs.avail_out;
			}
		}

		gis->z_err = inflate(&gis->zs, Z_NO_FLUSH);
	}

	if (gis->z_err != Z_OK && gis->z_err != Z_STREAM_END)
		return -1;

	return size - gis->zs.avail_out;
}

static void gz_close(void *stream)
{
	struct apk_gzip_istream *gis =
		container_of(stream, struct apk_gzip_istream, is);

	inflateEnd(&gis->zs);
Timo Teräs's avatar
Timo Teräs committed
68 69
	if (gis->autoclose)
		gis->bs->close(gis->bs, NULL, NULL);
70 71 72
	free(gis);
}

Timo Teräs's avatar
Timo Teräs committed
73
struct apk_istream *apk_bstream_gunzip(struct apk_bstream *bs, int autoclose)
74 75 76
{
	struct apk_gzip_istream *gis;

77 78 79
	if (bs == NULL)
		return NULL;

80 81 82 83 84 85 86 87 88
	gis = malloc(sizeof(struct apk_gzip_istream));
	if (gis == NULL)
		return NULL;

	*gis = (struct apk_gzip_istream) {
		.is.read = gz_read,
		.is.close = gz_close,
		.bs = bs,
		.z_err = 0,
Timo Teräs's avatar
Timo Teräs committed
89
		.autoclose = autoclose,
90 91 92 93 94 95 96 97 98 99
	};

	if (inflateInit2(&gis->zs, 15+32) != Z_OK) {
		free(gis);
		return NULL;
	}

	return &gis->is;
}