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