Commit 9a89f573 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen
Browse files

add fileutils: classes for error-checked reading and writing of files

the internal use of SaveFile ensures that we don't leave truncated
pre-existing files behind if the disk is full or something else bad
happens.

Task-number: QTCREATORBUG-1619
parent 1757217d
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#include "fileutils.h"
#include "savefile.h"
#include "qtcassert.h"
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTemporaryFile>
#include <QtCore/QDataStream>
#include <QtCore/QTextStream>
#include <QtCore/QXmlStreamWriter>
#include <QtGui/QMessageBox>
namespace Utils {
QByteArray FileReader::fetchQrc(const QString &fileName)
{
QTC_ASSERT(fileName.startsWith(QLatin1Char(':')), return QByteArray())
QFile file(fileName);
bool ok = file.open(QIODevice::ReadOnly);
QTC_ASSERT(ok, qWarning() << fileName << "not there!"; return QByteArray())
return file.readAll();
}
bool FileReader::fetch(const QString &fileName, QIODevice::OpenMode mode)
{
QTC_ASSERT(!(mode & ~(QIODevice::ReadOnly | QIODevice::Text)), return false)
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | mode)) {
m_errorString = tr("Cannot open %1 for reading: %2").arg(
QDir::toNativeSeparators(fileName), file.errorString());
return false;
}
m_data = file.readAll();
if (file.error() != QFile::NoError) {
m_errorString = tr("Cannot read %1: %2").arg(
QDir::toNativeSeparators(fileName), file.errorString());
return false;
}
return true;
}
bool FileReader::fetch(const QString &fileName, QIODevice::OpenMode mode, QString *errorString)
{
if (fetch(fileName, mode))
return true;
if (errorString)
*errorString = m_errorString;
return false;
}
bool FileReader::fetch(const QString &fileName, QIODevice::OpenMode mode, QWidget *parent)
{
if (fetch(fileName, mode))
return true;
if (parent)
QMessageBox::critical(parent, tr("File Error"), m_errorString);
return false;
}
FileSaverBase::FileSaverBase()
: m_hasError(false)
{
}
FileSaverBase::~FileSaverBase()
{
delete m_file;
}
bool FileSaverBase::finalize()
{
m_file->close();
setResult(m_file->error() == QFile::NoError);
// We delete the object, so it is really closed even if it is a QTemporaryFile.
delete m_file;
m_file = 0;
return !m_hasError;
}
bool FileSaverBase::finalize(QString *errStr)
{
if (finalize())
return true;
if (errStr)
*errStr = errorString();
return false;
}
bool FileSaverBase::finalize(QWidget *parent)
{
if (finalize())
return true;
QMessageBox::critical(parent, tr("File Error"), errorString());
return false;
}
bool FileSaverBase::write(const char *data, int len)
{
if (m_hasError)
return false;
return setResult(m_file->write(data, len) == len);
}
bool FileSaverBase::write(const QByteArray &bytes)
{
if (m_hasError)
return false;
return setResult(m_file->write(bytes) == bytes.count());
}
bool FileSaverBase::setResult(bool ok)
{
if (!ok && !m_hasError) {
m_errorString = tr("Cannot write file %1. Disk full?").arg(
QDir::toNativeSeparators(m_fileName));
m_hasError = true;
}
return ok;
}
bool FileSaverBase::setResult(QTextStream *stream)
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
stream->flush();
return setResult(stream->status() == QTextStream::Ok);
#else
Q_UNUSED(stream)
return true;
#endif
}
bool FileSaverBase::setResult(QDataStream *stream)
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
stream->flush();
return setResult(stream->status() == QTextStream::Ok);
#else
Q_UNUSED(stream)
return true;
#endif
}
bool FileSaverBase::setResult(QXmlStreamWriter *stream)
{
#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
return setResult(!stream->hasError());
#else
Q_UNUSED(stream)
return true;
#endif
}
FileSaver::FileSaver(const QString &filename, QIODevice::OpenMode mode)
{
m_fileName = filename;
if (mode & (QIODevice::ReadOnly | QIODevice::Append)) {
m_file = new QFile(filename);
m_isSafe = false;
} else {
m_file = new SaveFile(filename);
m_isSafe = true;
}
if (!m_file->open(QIODevice::WriteOnly | mode)) {
QString err = QFile::exists(filename) ?
tr("Cannot overwrite file %1: %2") : tr("Cannot create file %1: %2");
m_errorString = err.arg(QDir::toNativeSeparators(filename), m_file->errorString());
m_hasError = true;
}
}
bool FileSaver::finalize()
{
if (!m_isSafe)
return FileSaverBase::finalize();
SaveFile *sf = static_cast<SaveFile *>(m_file);
if (m_hasError)
sf->rollback();
else
setResult(sf->commit());
delete sf;
m_file = 0;
return !m_hasError;
}
TempFileSaver::TempFileSaver(const QString &templ)
: m_autoRemove(true)
{
QTemporaryFile *tempFile = new QTemporaryFile();
if (!templ.isEmpty())
tempFile->setFileTemplate(templ);
tempFile->setAutoRemove(false);
if (!tempFile->open()) {
m_errorString = tr("Cannot create temporary file in %1: %2").arg(
QDir::toNativeSeparators(QFileInfo(tempFile->fileTemplate()).absolutePath()),
tempFile->errorString());
m_hasError = true;
}
m_file = tempFile;
m_fileName = tempFile->fileName();
}
TempFileSaver::~TempFileSaver()
{
delete m_file;
m_file = 0;
if (m_autoRemove)
QFile::remove(m_fileName);
}
} // namespace Utils
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/
#ifndef FILEUTILS_H
#define FILEUTILS_H
#include "utils_global.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QIODevice>
QT_BEGIN_NAMESPACE
class QFile;
class QTemporaryFile;
class QWidget;
class QTextStream;
class QDataStream;
class QXmlStreamWriter;
QT_END_NAMESPACE
namespace Utils {
class QTCREATOR_UTILS_EXPORT FileReader
{
Q_DECLARE_TR_FUNCTIONS(FileUtils) // sic!
public:
static QByteArray fetchQrc(const QString &fileName); // Only for internal resources
bool fetch(const QString &fileName, QIODevice::OpenMode mode = QIODevice::NotOpen); // QIODevice::ReadOnly is implicit
bool fetch(const QString &fileName, QIODevice::OpenMode mode, QString *errorString);
bool fetch(const QString &fileName, QString *errorString)
{ return fetch(fileName, QIODevice::NotOpen, errorString); }
bool fetch(const QString &fileName, QIODevice::OpenMode mode, QWidget *parent);
bool fetch(const QString &fileName, QWidget *parent)
{ return fetch(fileName, QIODevice::NotOpen, parent); }
const QByteArray &data() const { return m_data; }
const QString &errorString() const { return m_errorString; }
private:
QByteArray m_data;
QString m_errorString;
};
class QTCREATOR_UTILS_EXPORT FileSaverBase
{
Q_DECLARE_TR_FUNCTIONS(FileUtils) // sic!
public:
FileSaverBase();
virtual ~FileSaverBase();
QString fileName() const { return m_fileName; }
bool hasError() const { return m_hasError; }
QString errorString() const { return m_errorString; }
virtual bool finalize();
bool finalize(QString *errStr);
bool finalize(QWidget *parent);
bool write(const char *data, int len);
bool write(const QByteArray &bytes);
bool setResult(QTextStream *stream);
bool setResult(QDataStream *stream);
bool setResult(QXmlStreamWriter *stream);
bool setResult(bool ok);
protected:
QFile *m_file;
QString m_fileName;
QString m_errorString;
bool m_hasError;
private:
Q_DISABLE_COPY(FileSaverBase)
};
class QTCREATOR_UTILS_EXPORT FileSaver : public FileSaverBase
{
public:
explicit FileSaver(const QString &filename, QIODevice::OpenMode mode = QIODevice::NotOpen); // QIODevice::WriteOnly is implicit
virtual bool finalize();
using FileSaverBase::finalize;
QFile *file() { return m_file; }
private:
Q_DISABLE_COPY(FileSaver)
bool m_isSafe;
};
class QTCREATOR_UTILS_EXPORT TempFileSaver : public FileSaverBase
{
public:
explicit TempFileSaver(const QString &templ = QString());
~TempFileSaver();
QTemporaryFile *file() { return reinterpret_cast<QTemporaryFile *>(m_file); }
void setAutoRemove(bool on) { m_autoRemove = on; }
private:
Q_DISABLE_COPY(TempFileSaver)
bool m_autoRemove;
};
}
#endif // FILEUTILS_H
......@@ -34,6 +34,7 @@ SOURCES += $$PWD/environment.cpp \
$$PWD/submiteditorwidget.cpp \
$$PWD/synchronousprocess.cpp \
$$PWD/savefile.cpp \
$$PWD/fileutils.cpp \
$$PWD/submitfieldwidget.cpp \
$$PWD/consoleprocess.cpp \
$$PWD/uncommentselection.cpp \
......@@ -124,6 +125,7 @@ HEADERS += $$PWD/environment.h \
$$PWD/consoleprocess.h \
$$PWD/synchronousprocess.h \
$$PWD/savefile.h \
$$PWD/fileutils.h \
$$PWD/submitfieldwidget.h \
$$PWD/uncommentselection.h \
$$PWD/parameteraction.h \
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment