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