1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "BZipFileDevice.h"
17
18 #include <bzlib.h>
19
20 #include <iostream>
21
22 #include "base/Debug.h"
23
24 // for dup:
25 #ifdef _MSC_VER
26 #include <io.h>
27 #else
28 #include <unistd.h>
29 #endif
30
BZipFileDevice(QString fileName)31 BZipFileDevice::BZipFileDevice(QString fileName) :
32 m_fileName(fileName),
33 m_qfile(fileName),
34 m_file(nullptr),
35 m_bzFile(nullptr),
36 m_atEnd(true),
37 m_ok(true)
38 {
39 }
40
~BZipFileDevice()41 BZipFileDevice::~BZipFileDevice()
42 {
43 // SVDEBUG << "BZipFileDevice::~BZipFileDevice(" << m_fileName << ")" << endl;
44 if (m_bzFile) close();
45 }
46
47 bool
isOK() const48 BZipFileDevice::isOK() const
49 {
50 return m_ok;
51 }
52
53 bool
open(OpenMode mode)54 BZipFileDevice::open(OpenMode mode)
55 {
56 setErrorString("");
57
58 if (m_bzFile) {
59 setErrorString(tr("File is already open"));
60 return false;
61 }
62
63 if (mode & Append) {
64 setErrorString(tr("Append mode not supported"));
65 m_ok = false;
66 return false;
67 }
68
69 if ((mode & (ReadOnly | WriteOnly)) == 0) {
70 setErrorString(tr("File access mode not specified"));
71 m_ok = false;
72 return false;
73 }
74
75 if ((mode & ReadOnly) && (mode & WriteOnly)) {
76 setErrorString(tr("Read and write modes both specified"));
77 m_ok = false;
78 return false;
79 }
80
81 // This is all going to be a bit silly.
82 //
83 // We open the file with QFile so as not to have to worry about locale
84 // support ourselves (especially on Windows). Then we get a fd from
85 // QFile and "convert" it to a FILE* using fdopen because that is what
86 // the bz2 library needs for reading and writing an already-open file.
87 //
88 // fdopen takes over the fd it is given, and will close it when fclose
89 // is called. (We must call fclose, because it's needed to avoid
90 // leaking the file stream structure.)
91 //
92 // But QFile will also close its fd, either when we call QFile::close
93 // or on destruction -- there doesn't seem to be a way to avoid that
94 // for a file that QFile opened.
95 //
96 // So we have to add an extra dup() in to the fdopen to avoid a double
97 // close.
98 //
99 // Note that bz2 will *not* fclose the FILE* it was passed, so we
100 // don't have a problem with calling both bzWriteClose and fclose.
101
102 if (mode & WriteOnly) {
103
104 if (!m_qfile.open(QIODevice::WriteOnly)) {
105 setErrorString(tr("Failed to open file for writing"));
106 m_ok = false;
107 return false;
108 }
109
110 m_file = fdopen(dup(m_qfile.handle()), "wb");
111 if (!m_file) {
112 setErrorString(tr("Failed to open file handle for writing"));
113 m_qfile.close();
114 m_ok = false;
115 return false;
116 }
117
118 int bzError = BZ_OK;
119 m_bzFile = BZ2_bzWriteOpen(&bzError, m_file, 9, 0, 0);
120
121 if (!m_bzFile) {
122 fclose(m_file);
123 m_file = nullptr;
124 m_qfile.close();
125 setErrorString(tr("Failed to open bzip2 stream for writing"));
126 m_ok = false;
127 return false;
128 }
129
130 // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for writing" << endl;
131
132 setErrorString(QString());
133 setOpenMode(mode);
134 return true;
135 }
136
137 if (mode & ReadOnly) {
138
139 if (!m_qfile.open(QIODevice::ReadOnly)) {
140 setErrorString(tr("Failed to open file for reading"));
141 m_ok = false;
142 return false;
143 }
144
145 m_file = fdopen(dup(m_qfile.handle()), "rb");
146 if (!m_file) {
147 setErrorString(tr("Failed to open file handle for reading"));
148 m_ok = false;
149 return false;
150 }
151
152 int bzError = BZ_OK;
153 m_bzFile = BZ2_bzReadOpen(&bzError, m_file, 0, 0, nullptr, 0);
154
155 if (!m_bzFile) {
156 fclose(m_file);
157 m_file = nullptr;
158 m_qfile.close();
159 setErrorString(tr("Failed to open bzip2 stream for reading"));
160 m_ok = false;
161 return false;
162 }
163
164 // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for reading" << endl;
165
166 m_atEnd = false;
167
168 setErrorString(QString());
169 setOpenMode(mode);
170 return true;
171 }
172
173 setErrorString(tr("Internal error (open for neither read nor write)"));
174 m_ok = false;
175 return false;
176 }
177
178 void
close()179 BZipFileDevice::close()
180 {
181 if (!m_bzFile) {
182 setErrorString(tr("File not open"));
183 m_ok = false;
184 return;
185 }
186
187 int bzError = BZ_OK;
188
189 if (openMode() & WriteOnly) {
190 unsigned int in = 0, out = 0;
191 BZ2_bzWriteClose(&bzError, m_bzFile, 0, &in, &out);
192 // cerr << "Wrote bzip2 stream (in=" << in << ", out=" << out << ")" << endl;
193 if (bzError != BZ_OK) {
194 setErrorString(tr("bzip2 stream write close error"));
195 }
196 fclose(m_file);
197 m_qfile.close();
198 m_bzFile = nullptr;
199 m_file = nullptr;
200 m_ok = false;
201 return;
202 }
203
204 if (openMode() & ReadOnly) {
205 BZ2_bzReadClose(&bzError, m_bzFile);
206 if (bzError != BZ_OK) {
207 setErrorString(tr("bzip2 stream read close error"));
208 }
209 fclose(m_file);
210 m_qfile.close();
211 m_bzFile = nullptr;
212 m_file = nullptr;
213 m_ok = false;
214 return;
215 }
216
217 setErrorString(tr("Internal error (close for neither read nor write)"));
218 return;
219 }
220
221 qint64
readData(char * data,qint64 maxSize)222 BZipFileDevice::readData(char *data, qint64 maxSize)
223 {
224 if (m_atEnd) return 0;
225
226 int bzError = BZ_OK;
227 int read = BZ2_bzRead(&bzError, m_bzFile, data, int(maxSize));
228
229 // SVDEBUG << "BZipFileDevice::readData: requested " << maxSize << ", read " << read << endl;
230
231 if (bzError != BZ_OK) {
232 if (bzError != BZ_STREAM_END) {
233 cerr << "BZipFileDevice::readData: error condition" << endl;
234 setErrorString(tr("bzip2 stream read error"));
235 m_ok = false;
236 return -1;
237 } else {
238 // SVDEBUG << "BZipFileDevice::readData: reached end of file" << endl;
239 m_atEnd = true;
240 }
241 }
242
243 return read;
244 }
245
246 qint64
writeData(const char * data,qint64 maxSize)247 BZipFileDevice::writeData(const char *data, qint64 maxSize)
248 {
249 int bzError = BZ_OK;
250 BZ2_bzWrite(&bzError, m_bzFile, (void *)data, int(maxSize));
251
252 // SVDEBUG << "BZipFileDevice::writeData: " << maxSize << " to write" << endl;
253
254 if (bzError != BZ_OK) {
255 cerr << "BZipFileDevice::writeData: error condition" << endl;
256 setErrorString("bzip2 stream write error");
257 m_ok = false;
258 return -1;
259 }
260
261 // SVDEBUG << "BZipFileDevice::writeData: wrote " << maxSize << endl;
262
263 return maxSize;
264 }
265
266