1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qbs.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include <language/scriptengine.h>
41 #include <logging/translator.h>
42 #include <tools/hostosinfo.h>
43
44 #include <QtCore/qfile.h>
45 #include <QtCore/qfileinfo.h>
46 #include <QtCore/qobject.h>
47 #include <QtCore/qtextstream.h>
48 #include <QtCore/qvariant.h>
49
50 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
51 #include <QtCore5Compat/qtextcodec.h>
52 #else
53 #include <QtCore/qtextcodec.h>
54 #endif
55
56 #include <QtScript/qscriptable.h>
57 #include <QtScript/qscriptengine.h>
58 #include <QtScript/qscriptvalue.h>
59
60 namespace qbs {
61 namespace Internal {
62
63 class TextFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject
64 {
65 Q_OBJECT
66 Q_ENUMS(OpenMode)
67 public:
68 enum OpenMode
69 {
70 ReadOnly = 1,
71 WriteOnly = 2,
72 ReadWrite = ReadOnly | WriteOnly,
73 Append = 4
74 };
75
76 static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
77
78 Q_INVOKABLE void close();
79 Q_INVOKABLE QString filePath();
80 Q_INVOKABLE void setCodec(const QString &codec);
81 Q_INVOKABLE QString readLine();
82 Q_INVOKABLE QString readAll();
83 Q_INVOKABLE bool atEof() const;
84 Q_INVOKABLE void truncate();
85 Q_INVOKABLE void write(const QString &str);
86 Q_INVOKABLE void writeLine(const QString &str);
87
88 private:
89 TextFile(QScriptContext *context, const QString &filePath, OpenMode mode = ReadOnly,
90 const QString &codec = QLatin1String("UTF-8"));
91
92 bool checkForClosed() const;
93
94 // ResourceAcquiringScriptObject implementation
95 void releaseResources() override;
96
97 std::unique_ptr<QFile> m_file;
98 QTextCodec *m_codec = nullptr;
99 };
100
ctor(QScriptContext * context,QScriptEngine * engine)101 QScriptValue TextFile::ctor(QScriptContext *context, QScriptEngine *engine)
102 {
103 TextFile *t;
104 switch (context->argumentCount()) {
105 case 0:
106 return context->throwError(Tr::tr("TextFile constructor needs path of file to be opened."));
107 case 1:
108 t = new TextFile(context, context->argument(0).toString());
109 break;
110 case 2:
111 t = new TextFile(context,
112 context->argument(0).toString(),
113 static_cast<OpenMode>(context->argument(1).toInt32())
114 );
115 break;
116 case 3:
117 t = new TextFile(context,
118 context->argument(0).toString(),
119 static_cast<OpenMode>(context->argument(1).toInt32()),
120 context->argument(2).toString()
121 );
122 break;
123 default:
124 return context->throwError(Tr::tr("TextFile constructor takes at most three parameters."));
125 }
126
127 const auto se = static_cast<ScriptEngine *>(engine);
128 se->addResourceAcquiringScriptObject(t);
129 const DubiousContextList dubiousContexts({
130 DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
131 });
132 se->checkContext(QStringLiteral("qbs.TextFile"), dubiousContexts);
133 se->setUsesIo();
134
135 return engine->newQObject(t, QScriptEngine::QtOwnership);
136 }
137
TextFile(QScriptContext * context,const QString & filePath,OpenMode mode,const QString & codec)138 TextFile::TextFile(QScriptContext *context, const QString &filePath, OpenMode mode,
139 const QString &codec)
140 {
141 Q_UNUSED(codec)
142 Q_ASSERT(thisObject().engine() == engine());
143
144 m_file = std::make_unique<QFile>(filePath);
145 const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
146 m_codec = newCodec ? newCodec : QTextCodec::codecForName("UTF-8");
147 QIODevice::OpenMode m = QIODevice::NotOpen;
148 if (mode & ReadOnly)
149 m |= QIODevice::ReadOnly;
150 if (mode & WriteOnly)
151 m |= QIODevice::WriteOnly;
152 if (mode & Append)
153 m |= QIODevice::Append;
154 m |= QIODevice::Text;
155 if (Q_UNLIKELY(!m_file->open(m))) {
156 context->throwError(Tr::tr("Unable to open file '%1': %2")
157 .arg(filePath, m_file->errorString()));
158 m_file.reset();
159 }
160 }
161
close()162 void TextFile::close()
163 {
164 if (checkForClosed())
165 return;
166 m_file->close();
167 m_file.reset();
168 }
169
filePath()170 QString TextFile::filePath()
171 {
172 if (checkForClosed())
173 return {};
174 return QFileInfo(*m_file).absoluteFilePath();
175 }
176
setCodec(const QString & codec)177 void TextFile::setCodec(const QString &codec)
178 {
179 if (checkForClosed())
180 return;
181 const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
182 if (newCodec)
183 m_codec = newCodec;
184 }
185
readLine()186 QString TextFile::readLine()
187 {
188 if (checkForClosed())
189 return {};
190 auto result = m_codec->toUnicode(m_file->readLine());
191 if (!result.isEmpty() && result.back() == QLatin1Char('\n'))
192 result.chop(1);
193 return result;
194 }
195
readAll()196 QString TextFile::readAll()
197 {
198 if (checkForClosed())
199 return {};
200 return m_codec->toUnicode(m_file->readAll());
201 }
202
atEof() const203 bool TextFile::atEof() const
204 {
205 if (checkForClosed())
206 return true;
207 return m_file->atEnd();
208 }
209
truncate()210 void TextFile::truncate()
211 {
212 if (checkForClosed())
213 return;
214 m_file->resize(0);
215 }
216
write(const QString & str)217 void TextFile::write(const QString &str)
218 {
219 if (checkForClosed())
220 return;
221 m_file->write(m_codec->fromUnicode(str));
222 }
223
writeLine(const QString & str)224 void TextFile::writeLine(const QString &str)
225 {
226 if (checkForClosed())
227 return;
228 m_file->write(m_codec->fromUnicode(str));
229 m_file->putChar('\n');
230 }
231
checkForClosed() const232 bool TextFile::checkForClosed() const
233 {
234 if (m_file)
235 return false;
236 QScriptContext *ctx = context();
237 if (ctx)
238 ctx->throwError(Tr::tr("Access to TextFile object that was already closed."));
239 return true;
240 }
241
releaseResources()242 void TextFile::releaseResources()
243 {
244 close();
245 deleteLater();
246 }
247
248 } // namespace Internal
249 } // namespace qbs
250
initializeJsExtensionTextFile(QScriptValue extensionObject)251 void initializeJsExtensionTextFile(QScriptValue extensionObject)
252 {
253 using namespace qbs::Internal;
254 QScriptEngine *engine = extensionObject.engine();
255 QScriptValue obj = engine->newQMetaObject(&TextFile::staticMetaObject,
256 engine->newFunction(&TextFile::ctor));
257 extensionObject.setProperty(QStringLiteral("TextFile"), obj);
258 }
259
260 Q_DECLARE_METATYPE(qbs::Internal::TextFile *)
261
262 #include "textfile.moc"
263