Chris@148: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@148: Chris@148: /* Chris@148: Sonic Visualiser Chris@148: An audio file viewer and annotation editor. Chris@148: Centre for Digital Music, Queen Mary, University of London. Chris@148: This file copyright 2006 Chris Cannam. Chris@148: Chris@148: This program is free software; you can redistribute it and/or Chris@148: modify it under the terms of the GNU General Public License as Chris@148: published by the Free Software Foundation; either version 2 of the Chris@148: License, or (at your option) any later version. See the file Chris@148: COPYING included with this distribution for more information. Chris@148: */ Chris@148: Chris@148: #include "BZipFileDevice.h" Chris@148: Chris@148: #include Chris@148: Chris@148: #include Chris@148: Chris@843: #include "base/Debug.h" Chris@843: Chris@1383: // for dup: Chris@1381: #ifdef _MSC_VER Chris@1381: #include Chris@1383: #else Chris@1383: #include Chris@1381: #endif Chris@1381: Chris@148: BZipFileDevice::BZipFileDevice(QString fileName) : Chris@148: m_fileName(fileName), Chris@1348: m_qfile(fileName), Chris@1582: m_file(nullptr), Chris@1582: m_bzFile(nullptr), Chris@207: m_atEnd(true), Chris@207: m_ok(true) Chris@148: { Chris@148: } Chris@148: Chris@148: BZipFileDevice::~BZipFileDevice() Chris@148: { Chris@690: // SVDEBUG << "BZipFileDevice::~BZipFileDevice(" << m_fileName << ")" << endl; Chris@148: if (m_bzFile) close(); Chris@148: } Chris@148: Chris@148: bool Chris@207: BZipFileDevice::isOK() const Chris@207: { Chris@207: return m_ok; Chris@207: } Chris@207: Chris@207: bool Chris@148: BZipFileDevice::open(OpenMode mode) Chris@148: { Chris@203: setErrorString(""); Chris@203: Chris@148: if (m_bzFile) { Chris@148: setErrorString(tr("File is already open")); Chris@148: return false; Chris@148: } Chris@148: Chris@148: if (mode & Append) { Chris@148: setErrorString(tr("Append mode not supported")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@148: if ((mode & (ReadOnly | WriteOnly)) == 0) { Chris@148: setErrorString(tr("File access mode not specified")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@148: if ((mode & ReadOnly) && (mode & WriteOnly)) { Chris@148: setErrorString(tr("Read and write modes both specified")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@1381: // This is all going to be a bit silly. Chris@1381: // Chris@1381: // We open the file with QFile so as not to have to worry about locale Chris@1381: // support ourselves (especially on Windows). Then we get a fd from Chris@1381: // QFile and "convert" it to a FILE* using fdopen because that is what Chris@1381: // the bz2 library needs for reading and writing an already-open file. Chris@1381: // Chris@1381: // fdopen takes over the fd it is given, and will close it when fclose Chris@1381: // is called. (We must call fclose, because it's needed to avoid Chris@1381: // leaking the file stream structure.) Chris@1381: // Chris@1381: // But QFile will also close its fd, either when we call QFile::close Chris@1381: // or on destruction -- there doesn't seem to be a way to avoid that Chris@1381: // for a file that QFile opened. Chris@1381: // Chris@1381: // So we have to add an extra dup() in to the fdopen to avoid a double Chris@1381: // close. Chris@1381: // Chris@1381: // Note that bz2 will *not* fclose the FILE* it was passed, so we Chris@1381: // don't have a problem with calling both bzWriteClose and fclose. Chris@1381: Chris@148: if (mode & WriteOnly) { Chris@148: Chris@1348: if (!m_qfile.open(QIODevice::WriteOnly)) { Chris@1348: setErrorString(tr("Failed to open file for writing")); Chris@1348: m_ok = false; Chris@1348: return false; Chris@1348: } Chris@1348: Chris@1381: m_file = fdopen(dup(m_qfile.handle()), "wb"); Chris@148: if (!m_file) { Chris@1348: setErrorString(tr("Failed to open file handle for writing")); Chris@1348: m_qfile.close(); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@148: int bzError = BZ_OK; Chris@148: m_bzFile = BZ2_bzWriteOpen(&bzError, m_file, 9, 0, 0); Chris@148: Chris@148: if (!m_bzFile) { Chris@148: fclose(m_file); Chris@1582: m_file = nullptr; Chris@1348: m_qfile.close(); Chris@148: setErrorString(tr("Failed to open bzip2 stream for writing")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@843: // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for writing" << endl; Chris@148: Chris@148: setErrorString(QString()); Chris@148: setOpenMode(mode); Chris@148: return true; Chris@148: } Chris@148: Chris@148: if (mode & ReadOnly) { Chris@148: Chris@1348: if (!m_qfile.open(QIODevice::ReadOnly)) { Chris@1348: setErrorString(tr("Failed to open file for reading")); Chris@1348: m_ok = false; Chris@1348: return false; Chris@1348: } Chris@1348: Chris@1381: m_file = fdopen(dup(m_qfile.handle()), "rb"); Chris@148: if (!m_file) { Chris@1348: setErrorString(tr("Failed to open file handle for reading")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@148: int bzError = BZ_OK; Chris@1582: m_bzFile = BZ2_bzReadOpen(&bzError, m_file, 0, 0, nullptr, 0); Chris@148: Chris@148: if (!m_bzFile) { Chris@148: fclose(m_file); Chris@1582: m_file = nullptr; Chris@1348: m_qfile.close(); Chris@148: setErrorString(tr("Failed to open bzip2 stream for reading")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@843: // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for reading" << endl; Chris@148: Chris@148: m_atEnd = false; Chris@148: Chris@148: setErrorString(QString()); Chris@148: setOpenMode(mode); Chris@148: return true; Chris@148: } Chris@148: Chris@148: setErrorString(tr("Internal error (open for neither read nor write)")); Chris@207: m_ok = false; Chris@148: return false; Chris@148: } Chris@148: Chris@148: void Chris@148: BZipFileDevice::close() Chris@148: { Chris@148: if (!m_bzFile) { Chris@148: setErrorString(tr("File not open")); Chris@207: m_ok = false; Chris@148: return; Chris@148: } Chris@148: Chris@148: int bzError = BZ_OK; Chris@148: Chris@148: if (openMode() & WriteOnly) { Chris@148: unsigned int in = 0, out = 0; Chris@148: BZ2_bzWriteClose(&bzError, m_bzFile, 0, &in, &out); Chris@1429: // cerr << "Wrote bzip2 stream (in=" << in << ", out=" << out << ")" << endl; Chris@1381: if (bzError != BZ_OK) { Chris@1381: setErrorString(tr("bzip2 stream write close error")); Chris@1381: } Chris@148: fclose(m_file); Chris@1348: m_qfile.close(); Chris@1582: m_bzFile = nullptr; Chris@1582: m_file = nullptr; Chris@207: m_ok = false; Chris@148: return; Chris@148: } Chris@148: Chris@148: if (openMode() & ReadOnly) { Chris@148: BZ2_bzReadClose(&bzError, m_bzFile); Chris@148: if (bzError != BZ_OK) { Chris@148: setErrorString(tr("bzip2 stream read close error")); Chris@148: } Chris@148: fclose(m_file); Chris@1348: m_qfile.close(); Chris@1582: m_bzFile = nullptr; Chris@1582: m_file = nullptr; Chris@207: m_ok = false; Chris@148: return; Chris@148: } Chris@148: Chris@148: setErrorString(tr("Internal error (close for neither read nor write)")); Chris@148: return; Chris@148: } Chris@148: Chris@148: qint64 Chris@148: BZipFileDevice::readData(char *data, qint64 maxSize) Chris@148: { Chris@148: if (m_atEnd) return 0; Chris@148: Chris@148: int bzError = BZ_OK; Chris@1038: int read = BZ2_bzRead(&bzError, m_bzFile, data, int(maxSize)); Chris@148: Chris@690: // SVDEBUG << "BZipFileDevice::readData: requested " << maxSize << ", read " << read << endl; Chris@148: Chris@148: if (bzError != BZ_OK) { Chris@148: if (bzError != BZ_STREAM_END) { Chris@843: cerr << "BZipFileDevice::readData: error condition" << endl; Chris@148: setErrorString(tr("bzip2 stream read error")); Chris@207: m_ok = false; Chris@148: return -1; Chris@148: } else { Chris@690: // SVDEBUG << "BZipFileDevice::readData: reached end of file" << endl; Chris@148: m_atEnd = true; Chris@148: } Chris@148: } Chris@148: Chris@148: return read; Chris@148: } Chris@148: Chris@148: qint64 Chris@148: BZipFileDevice::writeData(const char *data, qint64 maxSize) Chris@148: { Chris@148: int bzError = BZ_OK; Chris@1038: BZ2_bzWrite(&bzError, m_bzFile, (void *)data, int(maxSize)); Chris@148: Chris@690: // SVDEBUG << "BZipFileDevice::writeData: " << maxSize << " to write" << endl; Chris@148: Chris@148: if (bzError != BZ_OK) { Chris@843: cerr << "BZipFileDevice::writeData: error condition" << endl; Chris@148: setErrorString("bzip2 stream write error"); Chris@207: m_ok = false; Chris@148: return -1; Chris@148: } Chris@148: Chris@690: // SVDEBUG << "BZipFileDevice::writeData: wrote " << maxSize << endl; Chris@148: Chris@148: return maxSize; Chris@148: } Chris@148: