Bzip2ArchivePlugin.cxx 4.18 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 The Music Player Daemon Project
3
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19 20 21 22 23
 */

/**
  * single bz2 archive handling (requires libbz2)
  */

24
#include "config.h"
25
#include "Bzip2ArchivePlugin.hxx"
26 27 28
#include "../ArchivePlugin.hxx"
#include "../ArchiveFile.hxx"
#include "../ArchiveVisitor.hxx"
Max Kellermann's avatar
Max Kellermann committed
29
#include "input/InputStream.hxx"
30
#include "input/LocalOpen.hxx"
31
#include "fs/Path.hxx"
Max Kellermann's avatar
Max Kellermann committed
32 33

#include <bzlib.h>
34

35 36
#include <stdexcept>

37
class Bzip2ArchiveFile final : public ArchiveFile {
Max Kellermann's avatar
Max Kellermann committed
38
	std::string name;
39
	std::shared_ptr<InputStream> istream;
40

41
public:
42
	Bzip2ArchiveFile(Path path, InputStreamPtr &&_is)
43
		:name(path.GetBase().c_str()),
44
		 istream(std::move(_is)) {
45
		// remove .bz2 suffix
Max Kellermann's avatar
Max Kellermann committed
46
		const size_t len = name.length();
47
		if (len > 4)
Max Kellermann's avatar
Max Kellermann committed
48
			name.erase(len - 4);
49
	}
50

51
	virtual void Visit(ArchiveVisitor &visitor) override {
Max Kellermann's avatar
Max Kellermann committed
52
		visitor.VisitArchiveEntry(name.c_str());
53 54
	}

55
	InputStreamPtr OpenStream(const char *path,
56
				  Mutex &mutex) override;
57 58
};

59
class Bzip2InputStream final : public InputStream {
60
	std::shared_ptr<InputStream> input;
61

62
	bool eof = false;
63

64
	bz_stream bzstream;
65 66

	char buffer[5000];
67

68
public:
69 70
	Bzip2InputStream(const std::shared_ptr<InputStream> &_input,
			 const char *uri,
71
			 Mutex &mutex);
72 73
	~Bzip2InputStream();

74
	/* virtual methods from InputStream */
75
	bool IsEOF() noexcept override;
76
	size_t Read(void *ptr, size_t size) override;
77 78

private:
79
	void Open();
80
	bool FillBuffer();
81
};
82 83 84

/* single archive handling allocation helpers */

85 86
inline void
Bzip2InputStream::Open()
87
{
88 89 90
	bzstream.bzalloc = nullptr;
	bzstream.bzfree = nullptr;
	bzstream.opaque = nullptr;
91

92 93
	bzstream.next_in = (char *)buffer;
	bzstream.avail_in = 0;
94

95
	int ret = BZ2_bzDecompressInit(&bzstream, 0, 0);
96 97
	if (ret != BZ_OK)
		throw std::runtime_error("BZ2_bzDecompressInit() has failed");
98

99
	SetReady();
100 101 102 103
}

/* archive open && listing routine */

104
static std::unique_ptr<ArchiveFile>
105
bz2_open(Path pathname)
106
{
107
	static Mutex mutex;
108
	auto is = OpenLocalInputStream(pathname, mutex);
109
	return std::make_unique<Bzip2ArchiveFile>(pathname, std::move(is));
110 111 112 113
}

/* single archive handling */

114
Bzip2InputStream::Bzip2InputStream(const std::shared_ptr<InputStream> &_input,
115
				   const char *_uri,
116 117
				   Mutex &_mutex)
	:InputStream(_uri, _mutex),
118
	 input(_input)
119
{
120
	Open();
121 122 123 124
}

Bzip2InputStream::~Bzip2InputStream()
{
125
	BZ2_bzDecompressEnd(&bzstream);
126 127
}

128
InputStreamPtr
129
Bzip2ArchiveFile::OpenStream(const char *path,
130
			     Mutex &mutex)
131
{
132
	return std::make_unique<Bzip2InputStream>(istream, path, mutex);
133 134
}

135
inline bool
136
Bzip2InputStream::FillBuffer()
137
{
138
	if (bzstream.avail_in > 0)
139
		return true;
140

141
	size_t count = input->LockRead(buffer, sizeof(buffer));
142 143
	if (count == 0)
		return false;
144

145 146
	bzstream.next_in = buffer;
	bzstream.avail_in = count;
147
	return true;
148 149
}

150
size_t
151
Bzip2InputStream::Read(void *ptr, size_t length)
152
{
153 154
	const ScopeUnlock unlock(mutex);

155
	int bz_result;
156
	size_t nbytes = 0;
157

158
	if (eof)
159 160
		return 0;

161 162
	bzstream.next_out = (char *)ptr;
	bzstream.avail_out = length;
163

164
	do {
165
		if (!FillBuffer())
166
			return 0;
167

168
		bz_result = BZ2_bzDecompress(&bzstream);
169

170
		if (bz_result == BZ_STREAM_END) {
171
			eof = true;
172 173 174
			break;
		}

175 176
		if (bz_result != BZ_OK)
			throw std::runtime_error("BZ2_bzDecompress() has failed");
177
	} while (bzstream.avail_out == length);
178

179 180
	nbytes = length - bzstream.avail_out;
	offset += nbytes;
181

182
	return nbytes;
183 184
}

185
bool
186
Bzip2InputStream::IsEOF() noexcept
187
{
188
	return eof;
189 190 191 192 193 194
}

/* exported structures */

static const char *const bz2_extensions[] = {
	"bz2",
195
	nullptr
196 197
};

198
const ArchivePlugin bz2_archive_plugin = {
199 200 201 202 203
	"bz2",
	nullptr,
	nullptr,
	bz2_open,
	bz2_extensions,
204
};