annotate data/fileio/BZipFileDevice.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents 70e172e6cc59
children
rev   line source
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@1582 34 m_file(nullptr),
Chris@1582 35 m_bzFile(nullptr),
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@1582 123 m_file = nullptr;
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@1582 153 m_bzFile = BZ2_bzReadOpen(&bzError, m_file, 0, 0, nullptr, 0);
Chris@148 154
Chris@148 155 if (!m_bzFile) {
Chris@148 156 fclose(m_file);
Chris@1582 157 m_file = nullptr;
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@1582 198 m_bzFile = nullptr;
Chris@1582 199 m_file = nullptr;
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@1582 211 m_bzFile = nullptr;
Chris@1582 212 m_file = nullptr;
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