1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmldomfilewriter_p.h"
5#include <QtCore/QRandomGenerator>
6#include <QtCore/QScopeGuard>
7
8QT_BEGIN_NAMESPACE
9namespace QQmlJS {
10namespace Dom {
11
12FileWriter::Status FileWriter::write(QString tFile, function_ref<bool(QTextStream &)> write,
13 int nBk)
14{
15 if (shouldRemoveTempFile)
16 tempFile.remove();
17 tempFile.close();
18 shouldRemoveTempFile = false;
19 Q_ASSERT(status != Status::ShouldWrite);
20 status = Status::ShouldWrite;
21 targetFile = tFile;
22 newBkFiles.clear();
23 warnings.clear();
24
25 int i = 0;
26 const int maxAttempts = 20;
27 for (; i < maxAttempts; ++i) {
28 tempFile.setFileName(targetFile
29 + QString::number(QRandomGenerator::global()->generate(), base: 16).mid(position: 0, n: 8)
30 + QStringLiteral(u".tmp"));
31 if (tempFile.open(flags: QIODevice::ReadWrite | QIODevice::NewOnly))
32 break;
33 }
34 if (i == maxAttempts) {
35 warnings.append(t: tr(sourceText: "Could not create temp file for %1").arg(a: targetFile));
36 status = FileWriter::Status::SkippedDueToFailure;
37 return status;
38 }
39 shouldRemoveTempFile = true;
40 bool success = false;
41 QTextStream inF(&tempFile);
42 QT_TRY
43 {
44 auto cleanup = qScopeGuard(f: [this, &inF, &success, nBk] {
45 inF.flush();
46 tempFile.flush();
47 tempFile.close();
48 if (success) {
49 if (QFile::exists(fileName: targetFile)) {
50 // compareFiles
51 if (tempFile.open(flags: QIODevice::ReadOnly)) {
52 auto closeTmpF = qScopeGuard(f: [this] { tempFile.close(); });
53 QFile oldF(targetFile);
54 if (oldF.open(flags: QIODevice::ReadOnly)) {
55 bool same = true;
56 while (!tempFile.atEnd() && !oldF.atEnd()) {
57 QByteArray l1 = tempFile.readLine();
58 QByteArray l2 = oldF.readLine();
59 if (l1 != l2)
60 same = false;
61 }
62 if (tempFile.atEnd() && oldF.atEnd() && same) {
63 tempFile.remove();
64 shouldRemoveTempFile = false;
65 status = Status::SkippedEqual;
66 return;
67 }
68 }
69 }
70 }
71 // move to target
72 int i = 0;
73 const int maxAttempts = 10;
74 for (; i < maxAttempts; ++i) {
75 if (QFile::exists(fileName: targetFile)) {
76 // make place for targetFile
77 QString bkFileName;
78 if (nBk < 1) {
79 QFile::remove(fileName: targetFile);
80 } else if (nBk == 1) {
81 QString bkFileName = targetFile + QStringLiteral(u"~");
82 QFile::remove(fileName: bkFileName);
83 QFile::rename(oldName: targetFile, newName: bkFileName);
84 } else {
85 // f~ is the oldest, further backups at f1~ .. f<nBk>~
86 // keeping an empty place for the "next" backup
87 // f~ is never overwritten
88 int iBk = 0;
89 QString bkFileName = targetFile + QStringLiteral(u"~");
90 while (++iBk < nBk) {
91 if (QFile::exists(fileName: bkFileName))
92 bkFileName = targetFile + QString::number(iBk)
93 + QStringLiteral(u"~");
94 }
95 if (iBk == nBk)
96 QFile::remove(fileName: targetFile + QStringLiteral(u"1~"));
97 else
98 QFile::remove(fileName: targetFile + QString::number(++iBk)
99 + QStringLiteral(u"~"));
100 QFile::remove(fileName: bkFileName);
101 QFile::rename(oldName: targetFile, newName: bkFileName);
102 }
103 if (!bkFileName.isEmpty() && QFile::rename(oldName: targetFile, newName: bkFileName))
104 newBkFiles.append(t: bkFileName);
105 }
106 if (tempFile.rename(newName: targetFile)) {
107 status = Status::DidWrite;
108 shouldRemoveTempFile = false;
109 return;
110 }
111 }
112 warnings.append(
113 t: tr(sourceText: "Rename of file %1 to %2 failed").arg(args: tempFile.fileName(), args&: targetFile));
114 status = Status::SkippedDueToFailure;
115 } else {
116 warnings.append(t: tr(sourceText: "Error while writing"));
117 }
118 });
119 success = write(inF);
120 }
121 QT_CATCH(...)
122 {
123 warnings.append(t: tr(sourceText: "Exception trying to write file %1").arg(a: targetFile));
124 status = FileWriter::Status::SkippedDueToFailure;
125 }
126 if (status == Status::ShouldWrite)
127 status = Status::SkippedDueToFailure;
128 return status;
129}
130
131} // namespace Dom
132} // namespace QQmlJS
133QT_END_NAMESPACE
134
135#include "moc_qqmldomfilewriter_p.cpp"
136

source code of qtdeclarative/src/qmldom/qqmldomfilewriter.cpp