aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom/qqmldomfilewriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmldom/qqmldomfilewriter.cpp')
-rw-r--r--src/qmldom/qqmldomfilewriter.cpp135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/qmldom/qqmldomfilewriter.cpp b/src/qmldom/qqmldomfilewriter.cpp
new file mode 100644
index 0000000000..82f2aca496
--- /dev/null
+++ b/src/qmldom/qqmldomfilewriter.cpp
@@ -0,0 +1,135 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qqmldomfilewriter_p.h"
+#include <QtCore/QRandomGenerator>
+#include <QtCore/QScopeGuard>
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+FileWriter::Status FileWriter::write(const QString &tFile, function_ref<bool(QTextStream &)> write,
+ int nBk)
+{
+ if (shouldRemoveTempFile)
+ tempFile.remove();
+ tempFile.close();
+ shouldRemoveTempFile = false;
+ Q_ASSERT(status != Status::ShouldWrite);
+ status = Status::ShouldWrite;
+ targetFile = tFile;
+ newBkFiles.clear();
+ warnings.clear();
+
+ int i = 0;
+ const int maxAttempts = 20;
+ for (; i < maxAttempts; ++i) {
+ tempFile.setFileName(targetFile
+ + QString::number(QRandomGenerator::global()->generate(), 16).mid(0, 8)
+ + QStringLiteral(u".tmp"));
+ if (tempFile.open(QIODevice::ReadWrite | QIODevice::NewOnly))
+ break;
+ }
+ if (i == maxAttempts) {
+ warnings.append(tr("Could not create temp file for %1").arg(targetFile));
+ status = FileWriter::Status::SkippedDueToFailure;
+ return status;
+ }
+ shouldRemoveTempFile = true;
+ bool success = false;
+ QTextStream inF(&tempFile);
+ QT_TRY
+ {
+ auto cleanup = qScopeGuard([this, &inF, &success, nBk] {
+ inF.flush();
+ tempFile.flush();
+ tempFile.close();
+ if (success) {
+ if (QFile::exists(targetFile)) {
+ // compareFiles
+ if (tempFile.open(QIODevice::ReadOnly)) {
+ auto closeTmpF = qScopeGuard([this] { tempFile.close(); });
+ QFile oldF(targetFile);
+ if (oldF.open(QIODevice::ReadOnly)) {
+ bool same = true;
+ while (!tempFile.atEnd() && !oldF.atEnd()) {
+ QByteArray l1 = tempFile.readLine();
+ QByteArray l2 = oldF.readLine();
+ if (l1 != l2)
+ same = false;
+ }
+ if (tempFile.atEnd() && oldF.atEnd() && same) {
+ tempFile.remove();
+ shouldRemoveTempFile = false;
+ status = Status::SkippedEqual;
+ return;
+ }
+ }
+ }
+ }
+ // move to target
+ int i = 0;
+ const int maxAttempts = 10;
+ for (; i < maxAttempts; ++i) {
+ if (QFile::exists(targetFile)) {
+ // make place for targetFile
+ QString bkFileName;
+ if (nBk < 1) {
+ QFile::remove(targetFile);
+ } else if (nBk == 1) {
+ QString bkFileName = targetFile + QStringLiteral(u"~");
+ QFile::remove(bkFileName);
+ QFile::rename(targetFile, bkFileName);
+ } else {
+ // f~ is the oldest, further backups at f1~ .. f<nBk>~
+ // keeping an empty place for the "next" backup
+ // f~ is never overwritten
+ int iBk = 0;
+ QString bkFileName = targetFile + QStringLiteral(u"~");
+ while (++iBk < nBk) {
+ if (QFile::exists(bkFileName))
+ bkFileName = targetFile + QString::number(iBk)
+ + QStringLiteral(u"~");
+ }
+ if (iBk == nBk)
+ QFile::remove(targetFile + QStringLiteral(u"1~"));
+ else
+ QFile::remove(targetFile + QString::number(++iBk)
+ + QStringLiteral(u"~"));
+ QFile::remove(bkFileName);
+ QFile::rename(targetFile, bkFileName);
+ }
+ if (!bkFileName.isEmpty() && QFile::rename(targetFile, bkFileName))
+ newBkFiles.append(bkFileName);
+ }
+ if (tempFile.rename(targetFile)) {
+ status = Status::DidWrite;
+ shouldRemoveTempFile = false;
+ return;
+ }
+ }
+ warnings.append(
+ tr("Rename of file %1 to %2 failed").arg(tempFile.fileName(), targetFile));
+ status = Status::SkippedDueToFailure;
+ } else {
+ warnings.append(tr("Error while writing"));
+ }
+ });
+ success = write(inF);
+ }
+ QT_CATCH(...)
+ {
+ warnings.append(tr("Exception trying to write file %1").arg(targetFile));
+ status = FileWriter::Status::SkippedDueToFailure;
+ }
+ if (status == Status::ShouldWrite)
+ status = Status::SkippedDueToFailure;
+ return status;
+}
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#include "moc_qqmldomfilewriter_p.cpp"