summaryrefslogtreecommitdiffstats
path: root/src/gzip.c
blob: 05d406505e3b98ab5ba3e8831a48821f53df460f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright (C) 2023  Patrick McDermott
 *
 * This file is part of opkg-opkg.
 *
 * opkg-opkg is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * opkg-opkg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with opkg-opkg.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <zlib.h>
#include "defs.h"
#include "gzip.h"

#define OPKG_OPK_GZIP_BUFFER_SIZE 8192

struct opkg_opk_gzip_state {
	FILE          *input_file;
	unsigned char  input_buffer[OPKG_OPK_GZIP_BUFFER_SIZE];
	z_stream       stream;
};

static int
_opkg_opk_gzip_init(struct opkg_opk_gzip_state *state)
{
	state->stream.zalloc = Z_NULL;
	state->stream.zfree  = Z_NULL;
	state->stream.opaque = Z_NULL;
	if (inflateInit(&state->stream) != Z_OK) {
		return OPKG_OPK_ERROR;
	}
	return OPKG_OPK_OK;
}

int
opkg_opk_gzip_init_from_file(struct opkg_opk_gzip_state *state,
		const char *file_name)
{
	state->input_file = fopen(file_name, "rb");
	if (state->input_file == NULL) {
		return OPKG_OPK_ERROR;
	}
	state->stream.next_in  = Z_NULL;
	state->stream.avail_in = 0;
	if (_opkg_opk_gzip_init(state) < OPKG_OPK_OK) {
		fclose(state->input_file);
		return OPKG_OPK_ERROR;
	}
	return OPKG_OPK_OK;
}

int
opkg_opk_gzip_init_from_memory(struct opkg_opk_gzip_state *state,
		unsigned char *input, size_t input_size)
{
	state->input_file = NULL;
	state->stream.next_in  = input;
	state->stream.avail_in = input_size;
	return _opkg_opk_gzip_init(state);
}

static int
_opkg_opk_gzip_next_record_from_file(struct opkg_opk_gzip_state *state)
{
	for (;;) {
		if (feof(state->input_file)) {
			fclose(state->input_file);
			state->input_file = NULL;
			inflateEnd(&state->stream);
			return OPKG_OPK_OK;
		}
		if (state->stream.avail_in == 0) {
			state->stream.avail_in = fread(state->input_buffer,
					1, OPKG_OPK_GZIP_BUFFER_SIZE,
					state->input_file);
			if (ferror(state->input_file)) {
				fclose(state->input_file);
				state->input_file = NULL;
				inflateEnd(&state->stream);
				return OPKG_OPK_ERROR;
			}
			state->stream.next_in = state->input_buffer;
		}
		if (state->stream.avail_out == 0) {
			return OPKG_OPK_OK;
		}
		switch (inflate(&state->stream, Z_SYNC_FLUSH)) {
			case Z_OK:
			case Z_STREAM_END:
			case Z_BUF_ERROR:
				break;
			default:
				fclose(state->input_file);
				state->input_file = NULL;
				inflateEnd(&state->stream);
				return OPKG_OPK_ERROR;
		}
	}
}

static int
_opkg_opk_gzip_next_record_from_memory(struct opkg_opk_gzip_state *state)
{
	switch (inflate(&state->stream, Z_SYNC_FLUSH)) {
		case Z_OK:
			return OPKG_OPK_OK;
		case Z_STREAM_END:
			return OPKG_OPK_END;
		default:
			inflateEnd(&state->stream);
			return OPKG_OPK_ERROR;
	}
}

int
opkg_opk_gzip_next_record(struct opkg_opk_gzip_state *state, void *record)
{
	state->stream.next_out  = record;
	state->stream.avail_out = 512;
	if (state->input_file != NULL) {
		return _opkg_opk_gzip_next_record_from_file(state);
	} else {
		return _opkg_opk_gzip_next_record_from_memory(state);
	}
}