BZipFileDevice.cpp
Go to the documentation of this file.
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 
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 
42 {
43 // SVDEBUG << "BZipFileDevice::~BZipFileDevice(" << m_fileName << ")" << endl;
44  if (m_bzFile) close();
45 }
46 
47 bool
49 {
50  return m_ok;
51 }
52 
53 bool
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
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
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
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 
qint64 writeData(const char *data, qint64 maxSize) override
BZipFileDevice(QString fileName)
virtual ~BZipFileDevice()
virtual bool isOK() const
bool open(OpenMode mode) override
qint64 readData(char *data, qint64 maxSize) override
void close() override
BZFILE * m_bzFile