File indexing completed on 2024-12-22 04:16:01
0001 /* 0002 * SPDX-FileCopyrightText: 2022 Sharaf Zaman <shzam@sdf.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "KisExiv2IODevice.h" 0008 0009 #include <QDebug> 0010 #include <QFileInfo> 0011 0012 KisExiv2IODevice::KisExiv2IODevice(QString path) 0013 : m_file(path) 0014 , m_mappedArea(nullptr) 0015 { 0016 } 0017 0018 KisExiv2IODevice::~KisExiv2IODevice() 0019 { 0020 m_file.close(); 0021 } 0022 0023 int KisExiv2IODevice::open() 0024 { 0025 if (m_file.isOpen()) { 0026 m_file.close(); 0027 } 0028 0029 // return zero if successful 0030 return !m_file.open(QFile::ReadWrite); 0031 } 0032 0033 int KisExiv2IODevice::close() 0034 { 0035 if (munmap() != 0) { 0036 return 1; 0037 } 0038 m_file.close(); 0039 return 0; 0040 } 0041 0042 #if EXIV2_TEST_VERSION(0,28,0) 0043 size_t KisExiv2IODevice::write(const Exiv2::byte *data, size_t wcount) 0044 #else 0045 long KisExiv2IODevice::write(const Exiv2::byte *data, long wcount) 0046 #endif 0047 { 0048 if (!m_file.isWritable()) { 0049 qWarning() << "KisExiv2IODevice: File not open for writing."; 0050 return 0; 0051 } 0052 const qint64 writeCount = m_file.write(reinterpret_cast<const char *>(data), wcount); 0053 if (writeCount > 0) { 0054 return writeCount; 0055 } 0056 0057 return 0; 0058 } 0059 0060 #if EXIV2_TEST_VERSION(0,28,0) 0061 size_t KisExiv2IODevice::write(Exiv2::BasicIo &src) 0062 #else 0063 long KisExiv2IODevice::write(Exiv2::BasicIo &src) 0064 #endif 0065 { 0066 if (static_cast<BasicIo *>(this) == &src) { 0067 return 0; 0068 } 0069 if (!src.isopen()) { 0070 return 0; 0071 } 0072 if (!m_file.isWritable()) { 0073 qWarning() << "KisExiv2IODevice: File not open for writing."; 0074 return 0; 0075 } 0076 Exiv2::byte buffer[4096]; 0077 long readCount = 0; 0078 long totalWriteCount = 0; 0079 while ((readCount = src.read(buffer, sizeof(buffer))) != 0) { 0080 totalWriteCount += write(buffer, readCount); 0081 } 0082 0083 return totalWriteCount; 0084 } 0085 0086 int KisExiv2IODevice::putb(Exiv2::byte data) 0087 { 0088 if (!m_file.isWritable()) { 0089 qWarning() << "KisExiv2IODevice: File not open for writing."; 0090 return 0; 0091 } 0092 if (m_file.putChar(data)) { 0093 return data; 0094 } else { 0095 return EOF; 0096 } 0097 } 0098 0099 #if EXIV2_TEST_VERSION(0,28,0) 0100 Exiv2::DataBuf KisExiv2IODevice::read(size_t rcount) 0101 #else 0102 Exiv2::DataBuf KisExiv2IODevice::read(long rcount) 0103 #endif 0104 { 0105 Exiv2::DataBuf buf(rcount); 0106 #if EXIV2_TEST_VERSION(0,28,0) 0107 const size_t readCount = read(buf.data(), buf.size()); 0108 buf.resize(readCount); 0109 #else 0110 const long readCount = read(buf.pData_, buf.size_); 0111 buf.size_ = readCount; 0112 #endif 0113 return buf; 0114 } 0115 0116 #if EXIV2_TEST_VERSION(0,28,0) 0117 size_t KisExiv2IODevice::read(Exiv2::byte *buf, size_t rcount) 0118 #else 0119 long KisExiv2IODevice::read(Exiv2::byte *buf, long rcount) 0120 #endif 0121 { 0122 const qint64 bytesRead = m_file.read(reinterpret_cast<char *>(buf), rcount); 0123 if (bytesRead > 0) { 0124 return bytesRead; 0125 } else { 0126 qWarning() << "KisExiv2IODevice: Couldn't read file:" << m_file.errorString(); 0127 // some error or EOF 0128 return 0; 0129 } 0130 } 0131 0132 int KisExiv2IODevice::getb() 0133 { 0134 char c; 0135 if (m_file.getChar(&c)) { 0136 return c; 0137 } else { 0138 return EOF; 0139 } 0140 } 0141 0142 void KisExiv2IODevice::transfer(Exiv2::BasicIo &src) 0143 { 0144 bool isFileBased = (dynamic_cast<Exiv2::FileIo *>(&src) || dynamic_cast<KisExiv2IODevice *>(&src)); 0145 bool useFallback = false; 0146 0147 if (isFileBased) { 0148 const QString srcPath = QString::fromStdString(src.path()); 0149 // use fallback if copying failed (e.g on Android :( ) 0150 useFallback = !renameToCurrent(srcPath); 0151 } 0152 0153 if (!isFileBased || useFallback) { 0154 const bool wasOpen = isopen(); 0155 const QIODevice::OpenMode oldMode = m_file.openMode(); 0156 0157 // this sets file positioner to the beginning. 0158 if (src.open() != 0) { 0159 qWarning() << "KisExiv2IODevice::transfer: Couldn't open src file" << QString::fromStdString(src.path()); 0160 return; 0161 } 0162 0163 if (!open(QIODevice::ReadWrite | QIODevice::Truncate)) { 0164 qWarning() << "KisExiv2IODevice::transfer: Couldn't open dest file" << filePathQString(); 0165 return; 0166 } 0167 write(src); 0168 src.close(); 0169 0170 if (wasOpen) { 0171 open(oldMode); 0172 } else { 0173 close(); 0174 } 0175 } 0176 } 0177 0178 #if defined(_MSC_VER) || EXIV2_TEST_VERSION(0,28,0) 0179 int KisExiv2IODevice::seek(int64_t offset, Exiv2::BasicIo::Position position) 0180 #else 0181 int KisExiv2IODevice::seek(long offset, Exiv2::BasicIo::Position position) 0182 #endif 0183 { 0184 qint64 pos = 0; 0185 switch (position) { 0186 case Exiv2::BasicIo::beg: 0187 pos = offset; 0188 break; 0189 case Exiv2::BasicIo::cur: 0190 pos = tell() + offset; 0191 break; 0192 case Exiv2::BasicIo::end: 0193 pos = size() + offset; 0194 break; 0195 } 0196 return m_file.seek(pos); 0197 } 0198 0199 Exiv2::byte *KisExiv2IODevice::mmap(bool isWriteable) 0200 { 0201 Q_UNUSED(isWriteable); 0202 0203 if (munmap() != 0) { 0204 qWarning() << "KisExiv2IODevice::mmap: Couldn't unmap the mapped file"; 0205 return nullptr; 0206 } 0207 0208 m_mappedArea = m_file.map(0, size(), QFile::NoOptions); 0209 if (!m_mappedArea) { 0210 // We show a warning, but originally we should be throwing an exception. 0211 qWarning() << "KisExiv2IODevice::mmap: Couldn't map the file" << m_file.fileName(); 0212 } 0213 return m_mappedArea; 0214 } 0215 0216 int KisExiv2IODevice::munmap() 0217 { 0218 if (m_mappedArea) { 0219 bool successful = m_file.unmap(m_mappedArea); 0220 m_mappedArea = nullptr; 0221 return !successful; 0222 } 0223 return 0; 0224 } 0225 0226 #if EXIV2_TEST_VERSION(0,28,0) 0227 void KisExiv2IODevice::populateFakeData() 0228 { 0229 return; 0230 } 0231 #endif 0232 0233 #if EXIV2_TEST_VERSION(0,28,0) 0234 size_t KisExiv2IODevice::tell() const 0235 #else 0236 long KisExiv2IODevice::tell() const 0237 #endif 0238 { 0239 return m_file.pos(); 0240 } 0241 0242 size_t KisExiv2IODevice::size() const 0243 { 0244 if (m_file.isWritable()) { 0245 m_file.flush(); 0246 } 0247 return m_file.size(); 0248 } 0249 0250 bool KisExiv2IODevice::isopen() const 0251 { 0252 return m_file.isOpen(); 0253 } 0254 0255 int KisExiv2IODevice::error() const 0256 { 0257 // zero if no error 0258 return m_file.error() != QFileDevice::NoError; 0259 } 0260 0261 bool KisExiv2IODevice::eof() const 0262 { 0263 return m_file.atEnd(); 0264 } 0265 0266 #if EXIV2_TEST_VERSION(0,28,0) 0267 const std::string& KisExiv2IODevice::path() const noexcept 0268 #else 0269 std::string KisExiv2IODevice::path() const 0270 #endif 0271 { 0272 return filePathQString().toStdString(); 0273 } 0274 0275 bool KisExiv2IODevice::open(QFile::OpenMode mode) 0276 { 0277 if (m_file.isOpen()) { 0278 m_file.close(); 0279 } 0280 return m_file.open(mode); 0281 } 0282 0283 bool KisExiv2IODevice::renameToCurrent(const QString srcPath) 0284 { 0285 QFile::Permissions permissions = QFile::permissions(filePathQString()); 0286 if (QFile::exists(filePathQString())) { 0287 QFile::remove(filePathQString()); 0288 } 0289 0290 if (!QFile(srcPath).rename(filePathQString())) { 0291 qWarning() << "KisExiv2IODevice:renameToCurrent Couldn't copy file from" << srcPath << "to" << filePathQString(); 0292 return false; 0293 } 0294 return QFile(filePathQString()).setPermissions(permissions); 0295 } 0296 0297 QString KisExiv2IODevice::filePathQString() const 0298 { 0299 return QFileInfo(m_file).absoluteFilePath(); 0300 }