/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the qmake application of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qmakevfs.h" #include "ioutils.h" using namespace QMakeInternal; #include #include #include #if QT_CONFIG(textcodec) #include #endif #define fL1S(s) QString::fromLatin1(s) QT_BEGIN_NAMESPACE QMakeVfs::QMakeVfs() #ifndef PROEVALUATOR_FULL : m_magicMissing(fL1S("missing")) , m_magicExisting(fL1S("existing")) #endif { #if QT_CONFIG(textcodec) m_textCodec = 0; #endif ref(); } QMakeVfs::~QMakeVfs() { deref(); } void QMakeVfs::ref() { #ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&s_mutex); #endif ++s_refCount; } void QMakeVfs::deref() { #ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&s_mutex); #endif if (!--s_refCount) { s_fileIdCounter = 0; s_fileIdMap.clear(); s_idFileMap.clear(); } } #ifdef PROPARSER_THREAD_SAFE QMutex QMakeVfs::s_mutex; #endif int QMakeVfs::s_refCount; QAtomicInt QMakeVfs::s_fileIdCounter; QHash QMakeVfs::s_fileIdMap; QHash QMakeVfs::s_idFileMap; int QMakeVfs::idForFileName(const QString &fn, VfsFlags flags) { #ifdef PROEVALUATOR_DUAL_VFS { # ifdef PROPARSER_THREAD_SAFE QMutexLocker locker(&m_vmutex); # endif int idx = (flags & VfsCumulative) ? 1 : 0; if (flags & VfsCreate) { int &id = m_virtualFileIdMap[idx][fn]; if (!id) { id = ++s_fileIdCounter; m_virtualIdFileMap[id] = fn; } return id; } int id = m_virtualFileIdMap[idx].value(fn); if (id || (flags & VfsCreatedOnly)) return id; } #endif #ifdef PROPARSER_THREAD_SAFE QMutexLocker locker(&s_mutex); #endif if (!(flags & VfsAccessedOnly)) { int &id = s_fileIdMap[fn]; if (!id) { id = ++s_fileIdCounter; s_idFileMap[id] = fn; } return id; } return s_fileIdMap.value(fn); } QString QMakeVfs::fileNameForId(int id) { #ifdef PROEVALUATOR_DUAL_VFS { # ifdef PROPARSER_THREAD_SAFE QMutexLocker locker(&m_vmutex); # endif const QString &fn = m_virtualIdFileMap.value(id); if (!fn.isEmpty()) return fn; } #endif #ifdef PROPARSER_THREAD_SAFE QMutexLocker locker(&s_mutex); #endif return s_idFileMap.value(id); } bool QMakeVfs::writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags, const QString &contents, QString *errStr) { #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif QString *cont = &m_files[id]; Q_UNUSED(flags) if (mode & QIODevice::Append) *cont += contents; else *cont = contents; Q_UNUSED(errStr) return true; #else QFileInfo qfi(fileNameForId(id)); if (!QDir::current().mkpath(qfi.path())) { *errStr = fL1S("Cannot create parent directory"); return false; } QByteArray bytes = contents.toLocal8Bit(); QFile cfile(qfi.filePath()); if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) { if (cfile.readAll() == bytes) { if (flags & VfsExecutable) { cfile.setPermissions(cfile.permissions() | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); } else { cfile.setPermissions(cfile.permissions() & ~(QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther)); } return true; } cfile.close(); } if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) { *errStr = cfile.errorString(); return false; } cfile.write(bytes); cfile.close(); if (cfile.error() != QFile::NoError) { *errStr = cfile.errorString(); return false; } if (flags & VfsExecutable) cfile.setPermissions(cfile.permissions() | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); return true; #endif } QMakeVfs::ReadResult QMakeVfs::readFile(int id, QString *contents, QString *errStr) { #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif auto it = m_files.constFind(id); if (it != m_files.constEnd()) { if (it->constData() == m_magicMissing.constData()) { *errStr = fL1S("No such file or directory"); return ReadNotFound; } if (it->constData() != m_magicExisting.constData()) { *contents = *it; return ReadOk; } } #endif QFile file(fileNameForId(id)); if (!file.open(QIODevice::ReadOnly)) { if (!file.exists()) { #ifndef PROEVALUATOR_FULL m_files[id] = m_magicMissing; #endif *errStr = fL1S("No such file or directory"); return ReadNotFound; } *errStr = file.errorString(); return ReadOtherError; } #ifndef PROEVALUATOR_FULL m_files[id] = m_magicExisting; #endif QByteArray bcont = file.readAll(); if (bcont.startsWith("\xef\xbb\xbf")) { // UTF-8 BOM will cause subtle errors *errStr = fL1S("Unexpected UTF-8 BOM"); return ReadOtherError; } *contents = #if QT_CONFIG(textcodec) m_textCodec ? m_textCodec->toUnicode(bcont) : #endif QString::fromLocal8Bit(bcont); return ReadOk; } bool QMakeVfs::exists(const QString &fn, VfsFlags flags) { #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif int id = idForFileName(fn, flags); auto it = m_files.constFind(id); if (it != m_files.constEnd()) return it->constData() != m_magicMissing.constData(); #else Q_UNUSED(flags) #endif bool ex = IoUtils::fileType(fn) == IoUtils::FileIsRegular; #ifndef PROEVALUATOR_FULL m_files[id] = ex ? m_magicExisting : m_magicMissing; #endif return ex; } #ifndef PROEVALUATOR_FULL // This should be called when the sources may have changed (e.g., VCS update). void QMakeVfs::invalidateCache() { # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif auto it = m_files.begin(), eit = m_files.end(); while (it != eit) { if (it->constData() == m_magicMissing.constData() ||it->constData() == m_magicExisting.constData()) it = m_files.erase(it); else ++it; } } // This should be called when generated files may have changed (e.g., actual build). void QMakeVfs::invalidateContents() { # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif m_files.clear(); } #endif #if QT_CONFIG(textcodec) void QMakeVfs::setTextCodec(const QTextCodec *textCodec) { m_textCodec = textCodec; } #endif QT_END_NAMESPACE