source: branches/vendor/trolltech/qt/current/tools/qdbus/qdbuscpp2xml/qdbuscpp2xml.cpp @ 2

Last change on this file since 2 was 2, checked in by Dmitry A. Kuminov, 12 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 13.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file.  Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file.  Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QByteArray>
43#include <QString>
44#include <QVarLengthArray>
45#include <QFile>
46#include <QProcess>
47#include <QMetaObject>
48#include <QList>
49#include <QRegExp>
50#include <QCoreApplication>
51#include <QLibraryInfo>
52
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include <stdlib.h>
57
58#include "qdbusconnection.h"    // for the Export* flags
59
60// copied from dbus-protocol.h:
61static const char docTypeHeader[] =
62    "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
63    "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
64
65// in qdbusxmlgenerator.cpp
66QT_BEGIN_NAMESPACE
67extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
68                                                       const QMetaObject *base, int flags);
69QT_END_NAMESPACE
70
71#define PROGRAMNAME     "qdbuscpp2xml"
72#define PROGRAMVERSION  "0.1"
73#define PROGRAMCOPYRIGHT "Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)."
74
75static QString outputFile;
76static int flags;
77
78static const char help[] =
79    "Usage: " PROGRAMNAME " [options...] [files...]\n"
80    "Parses the C++ source or header file containing a QObject-derived class and\n"
81    "produces the D-Bus Introspection XML."
82    "\n"
83    "Options:\n"
84    "  -p|-s|-m       Only parse scriptable Properties, Signals and Methods (slots)\n"
85    "  -P|-S|-M       Parse all Properties, Signals and Methods (slots)\n"
86    "  -a             Output all scriptable contents (equivalent to -psm)\n"
87    "  -A             Output all contents (equivalent to -PSM)\n"
88    "  -o <filename>  Write the output to file <filename>\n"
89    "  -h             Show this information\n"
90    "  -V             Show the program version and quit.\n"
91    "\n";
92
93class MocParser
94{
95    void parseError();
96    QByteArray readLine();
97    void loadIntData(uint *&data);
98    void loadStringData(char *&stringdata);
99
100    QIODevice *input;
101    const char *filename;
102    int lineNumber;
103public:
104    ~MocParser();
105    void parse(const char *filename, QIODevice *input, int lineNumber = 0);
106
107    QList<QMetaObject> objects;
108};
109
110void MocParser::parseError()
111{
112    fprintf(stderr, PROGRAMNAME ": error parsing input file '%s' line %d \n", filename, lineNumber);
113    exit(1);
114}
115
116QByteArray MocParser::readLine()
117{
118    ++lineNumber;
119    return input->readLine();
120}
121
122void MocParser::loadIntData(uint *&data)
123{
124    data = 0;                   // initialise
125    QVarLengthArray<uint> array;
126    QRegExp rx(QLatin1String("(\\d+|0x[0-9abcdef]+)"), Qt::CaseInsensitive);
127
128    while (!input->atEnd()) {
129        QString line = QLatin1String(readLine());
130        int pos = line.indexOf(QLatin1String("//"));
131        if (pos != -1)
132            line.truncate(pos); // drop comments
133
134        if (line == QLatin1String("};\n")) {
135            // end of data
136            data = new uint[array.count()];
137            memcpy(data, array.data(), array.count() * sizeof(*data));
138            return;
139        }
140
141        pos = 0;
142        while ((pos = rx.indexIn(line, pos)) != -1) {
143            QString num = rx.cap(1);
144            if (num.startsWith(QLatin1String("0x")))
145                array.append(num.mid(2).toUInt(0, 16));
146            else
147                array.append(num.toUInt());
148            pos += rx.matchedLength();
149        }
150    }
151
152    parseError();
153}
154
155void MocParser::loadStringData(char *&stringdata)
156{
157    stringdata = 0;
158    QVarLengthArray<char, 1024> array;
159
160    while (!input->atEnd()) {
161        QByteArray line = readLine();
162        if (line == "};\n") {
163            // end of data
164            stringdata = new char[array.count()];
165            memcpy(stringdata, array.data(), array.count() * sizeof(*stringdata));
166            return;
167        }
168
169        int start = line.indexOf('"');
170        if (start == -1)
171            parseError();
172
173        int len = line.length() - 1;
174        line.truncate(len);     // drop ending \n
175        if (line.at(len - 1) != '"')
176            parseError();
177
178        --len;
179        ++start;
180        for ( ; start < len; ++start)
181            if (line.at(start) == '\\') {
182                // parse escaped sequence
183                ++start;
184                if (start == len)
185                    parseError();
186
187                QChar c(QLatin1Char(line.at(start)));
188                if (!c.isDigit()) {
189                    switch (c.toLatin1()) {
190                    case 'a':
191                        array.append('\a');
192                        break;
193                    case 'b':
194                        array.append('\b');
195                        break;
196                    case 'f':
197                        array.append('\f');
198                        break;
199                    case 'n':
200                        array.append('\n');
201                        break;
202                    case 'r':
203                        array.append('\r');
204                        break;
205                    case 't':
206                        array.append('\t');
207                        break;
208                    case 'v':
209                        array.append('\v');
210                        break;
211                    case '\\':
212                    case '?':
213                    case '\'':
214                    case '"':
215                        array.append(c.toLatin1());
216                        break;
217
218                    case 'x':
219                        if (start + 2 <= len)
220                            parseError();
221                        array.append(char(line.mid(start + 1, 2).toInt(0, 16)));
222                        break;
223
224                    default:
225                        array.append(c.toLatin1());
226                        fprintf(stderr, PROGRAMNAME ": warning: invalid escape sequence '\\%c' found in input",
227                                c.toLatin1());
228                    }
229                } else {
230                    // octal
231                    QRegExp octal(QLatin1String("([0-7]+)"));
232                    if (octal.indexIn(QLatin1String(line), start) == -1)
233                        parseError();
234                    array.append(char(octal.cap(1).toInt(0, 8)));
235                }
236            } else {
237                array.append(line.at(start));
238            }
239    }
240
241    parseError();
242}
243
244void MocParser::parse(const char *fname, QIODevice *io, int lineNum)
245{
246    filename = fname;
247    input = io;
248    lineNumber = lineNum;
249
250    while (!input->atEnd()) {
251        QByteArray line = readLine();
252        if (line.startsWith("static const uint qt_meta_data_")) {
253            // start of new class data
254            uint *data;
255            loadIntData(data);
256
257            // find the start of the string data
258            do {
259                line = readLine();
260                if (input->atEnd())
261                    parseError();
262            } while (!line.startsWith("static const char qt_meta_stringdata_"));
263
264            char *stringdata;
265            loadStringData(stringdata);
266
267            QMetaObject mo;
268            mo.d.superdata = &QObject::staticMetaObject;
269            mo.d.stringdata = stringdata;
270            mo.d.data = data;
271            mo.d.extradata = 0;
272            objects.append(mo);
273        }
274    }
275
276    fname = 0;
277    input = 0;
278}
279
280MocParser::~MocParser()
281{
282    foreach (QMetaObject mo, objects) {
283        delete const_cast<char *>(mo.d.stringdata);
284        delete const_cast<uint *>(mo.d.data);
285    }
286}
287
288static void showHelp()
289{
290    printf("%s", help);
291    exit(0);
292}
293
294static void showVersion()
295{
296    printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
297    printf("D-Bus QObject-to-XML converter\n");
298    exit(0);
299}
300
301static void parseCmdLine(QStringList &arguments)
302{
303    for (int i = 1; i < arguments.count(); ++i) {
304        const QString arg = arguments.at(i);
305
306        if (arg == QLatin1String("--help"))
307            showHelp();
308
309        if (!arg.startsWith(QLatin1Char('-')))
310            continue;
311
312        char c = arg.count() == 2 ? arg.at(1).toLatin1() : char(0);
313        switch (c) {
314        case 'P':
315            flags |= QDBusConnection::ExportNonScriptableProperties;
316            // fall through
317        case 'p':
318            flags |= QDBusConnection::ExportScriptableProperties;
319            break;
320
321        case 'S':
322            flags |= QDBusConnection::ExportNonScriptableSignals;
323            // fall through
324        case 's':
325            flags |= QDBusConnection::ExportScriptableSignals;
326            break;
327
328        case 'M':
329            flags |= QDBusConnection::ExportNonScriptableSlots;
330            // fall through
331        case 'm':
332            flags |= QDBusConnection::ExportScriptableSlots;
333            break;
334
335        case 'A':
336            flags |= QDBusConnection::ExportNonScriptableContents;
337            // fall through
338        case 'a':
339            flags |= QDBusConnection::ExportScriptableContents;
340            break;
341
342        case 'o':
343            if (arguments.count() < i + 2 || arguments.at(i + 1).startsWith(QLatin1Char('-'))) {
344                printf("-o expects a filename\n");
345                exit(1);
346            }
347            outputFile = arguments.takeAt(i + 1);
348            break;
349
350        case 'h':
351        case '?':
352            showHelp();
353            break;
354
355        case 'V':
356            showVersion();
357            break;
358
359        default:
360            printf("unknown option: \"%s\"\n", qPrintable(arg));
361            exit(1);
362        }
363    }
364
365    if (flags == 0)
366        flags = QDBusConnection::ExportScriptableContents
367                | QDBusConnection::ExportNonScriptableContents;
368}
369
370int main(int argc, char **argv)
371{
372    QCoreApplication app(argc, argv);
373    QStringList args = app.arguments();
374
375    MocParser parser;
376    parseCmdLine(args);
377
378    for (int i = 1; i < args.count(); ++i) {
379        const QString arg = args.at(i);
380        if (arg.startsWith(QLatin1Char('-')))
381            continue;
382
383        QFile f(arg);
384        if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) {
385            fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
386                    qPrintable(arg), qPrintable(f.errorString()));
387            return 1;
388        }
389
390        f.readLine();
391
392        QByteArray line = f.readLine();
393        if (line.contains("Meta object code from reading C++ file"))
394            // this is a moc-generated file
395            parser.parse(argv[i], &f, 3);
396        else {
397            // run moc on this file
398            QProcess proc;
399            proc.start(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/moc"), QStringList() << QFile::decodeName(argv[i]), QIODevice::ReadOnly | QIODevice::Text);
400
401            if (!proc.waitForStarted()) {
402                fprintf(stderr, PROGRAMNAME ": could not execute moc! Aborting.\n");
403                return 1;
404            }
405
406            proc.closeWriteChannel();
407
408            if (!proc.waitForFinished() || proc.exitStatus() != QProcess::NormalExit ||
409                proc.exitCode() != 0) {
410                // output the moc errors:
411                fprintf(stderr, "%s", proc.readAllStandardError().constData());
412                fprintf(stderr, PROGRAMNAME ": exit code %d from moc. Aborting\n", proc.exitCode());
413                return 1;
414            }
415            fprintf(stderr, "%s", proc.readAllStandardError().constData());
416
417            parser.parse(argv[i], &proc, 1);
418        }
419
420        f.close();
421    }
422
423    QFile output;
424    if (outputFile.isEmpty()) {
425        output.open(stdout, QIODevice::WriteOnly);
426    } else {
427        output.setFileName(outputFile);
428        if (!output.open(QIODevice::WriteOnly)) {
429            fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s",
430                    qPrintable(outputFile), qPrintable(output.errorString()));
431            return 1;
432        }
433    }
434
435    output.write(docTypeHeader);
436    output.write("<node>\n");
437    foreach (QMetaObject mo, parser.objects) {
438        QString xml = qDBusGenerateMetaObjectXml(QString(), &mo, &QObject::staticMetaObject,
439                                                 flags);
440        output.write(xml.toLocal8Bit());
441    }
442    output.write("</node>\n");
443
444    return 0;
445}
446
Note: See TracBrowser for help on using the repository browser.