Revision control
++ // 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/qrandom.h>
++ #include <QtCore/qscopeguard.h>
++ #include <QtCore/qtextstream.h>
++
++ QT_BEGIN_NAMESPACE
++ namespace QQmlJS {
++ namespace Dom {
++
++ FileWriter::Status FileWriter::write(const QString &tFile, function_ref<bool(QTextStream &)> write)
++ {
++ if (shouldRemoveTempFile)
++ tempFile.remove();
++ tempFile.close();
++ shouldRemoveTempFile = false;
++ Q_ASSERT(status != Status::ShouldWrite);
++ status = Status::ShouldWrite;
++ targetFile = tFile;
++ 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] {
++ 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
++ if (QFile::exists(targetFile))
++ QFile::remove(targetFile);
++ if (tempFile.rename(targetFile)) {
++ status = Status::DidWrite;
++ shouldRemoveTempFile = false;
++ } else {
++ 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"