* qt/: Update to Subversion r548032.
[platform/upstream/dbus.git] / qt / tools / dbuscpp2xml.cpp
1 /* -*- C++ -*-
2  *
3  * Copyright (C) 2006 Trolltech AS. All rights reserved.
4  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #include <QByteArray>
23 #include <QString>
24 #include <QVarLengthArray>
25 #include <QFile>
26 #include <QProcess>
27 #include <QMetaObject>
28 #include <QList>
29 #include <QRegExp>
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <unistd.h>
35
36 #include "../src/qdbusconnection.h"    // for the Export* flags
37
38 // copied from dbus-protocol.h:
39 static const char docTypeHeader[] =
40     "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
41     "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n";
42
43 // in qdbusxmlgenerator.cpp
44 extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
45                                                        const QMetaObject *base, int flags);
46
47 #define PROGRAMNAME     "dbuscpp2xml"
48 #define PROGRAMVERSION  "0.1"
49 #define PROGRAMCOPYRIGHT "Copyright (C) 2006 Trolltech AS. All rights reserved."
50
51 static const char cmdlineOptions[] = "psmaPSMAo:hV";
52 static const char *outputFile;
53 static int flags;
54
55 static const char help[] =
56     "Usage: " PROGRAMNAME " [options...] [files...]\n"
57     "Parses the C++ source or header file containing a QObject-derived class and\n"
58     "produces the D-Bus Introspection XML."
59     "\n"
60     "Options:\n"
61     "  -p|-s|-m       Only parse scriptable Properties, Signals and Methods (slots)\n"
62     "  -P|-S|-M       Parse all Properties, Signals and Methods (slots)\n"
63     "  -a             Output all scriptable contents (equivalent to -psm)\n"
64     "  -A             Output all contents (equivalent to -PSM)\n"
65     "  -o <filename>  Write the output to file <filename>\n"
66     "  -h             Show this information\n"
67     "  -V             Show the program version and quit.\n"
68     "\n";
69
70 class MocParser
71 {
72     void parseError();
73     QByteArray readLine();
74     void loadIntData(uint *&data);
75     void loadStringData(char *&stringdata);
76
77     QIODevice *input;
78     const char *filename;
79     int line;
80 public:
81     ~MocParser();
82     void parse(const char *filename, QIODevice *input, int lineNumber = 0);
83
84     QList<QMetaObject> objects;
85 };
86     
87 void MocParser::parseError()
88 {
89     fprintf(stderr, PROGRAMNAME ": error parsing input file '%s' line %d \n", filename, line);
90     exit(1);
91 }
92
93 QByteArray MocParser::readLine()
94 {
95     ++line;
96     return input->readLine();
97 }
98
99 void MocParser::loadIntData(uint *&data)
100 {
101     data = 0;                   // initialise
102     QVarLengthArray<uint> array;
103     QRegExp rx("(\\d+|0x[0-9abcdef]+)", Qt::CaseInsensitive);
104
105     while (!input->atEnd()) {
106         QString line = QLatin1String(readLine());
107         int pos = line.indexOf("//");
108         if (pos != -1)
109             line.truncate(pos); // drop comments
110
111         if (line == "};\n") {
112             // end of data
113             data = new uint[array.count()];
114             memcpy(data, array.data(), array.count() * sizeof(*data));
115             return;
116         }
117
118         pos = 0;
119         while ((pos = rx.indexIn(line, pos)) != -1) {
120             QString num = rx.cap(1);
121             if (num.startsWith("0x"))
122                 array.append(num.mid(2).toUInt(0, 16));
123             else
124                 array.append(num.toUInt());
125             pos += rx.matchedLength();
126         }
127     }
128
129     parseError();
130 }
131
132 void MocParser::loadStringData(char *&stringdata)
133 {
134     stringdata = 0;
135     QVarLengthArray<char, 1024> array;
136
137     while (!input->atEnd()) {
138         QByteArray line = readLine();
139         if (line == "};\n") {
140             // end of data
141             stringdata = new char[array.count()];
142             memcpy(stringdata, array.data(), array.count() * sizeof(*stringdata));
143             return;
144         }
145
146         int start = line.indexOf('"');
147         if (start == -1)
148             parseError();
149
150         int len = line.length() - 1;
151         line.truncate(len);     // drop ending \n
152         if (line.at(len - 1) != '"')
153             parseError();
154
155         --len;
156         ++start;
157         for ( ; start < len; ++start)
158             if (line.at(start) == '\\') {
159                 // parse escaped sequence
160                 ++start;
161                 if (start == len)
162                     parseError();
163
164                 QChar c(QLatin1Char(line.at(start)));
165                 if (!c.isDigit()) {
166                     switch (c.toLatin1()) {
167                     case 'a':
168                         array.append('\a');
169                         break;
170                     case 'b':
171                         array.append('\b');
172                         break;
173                     case 'f':
174                         array.append('\f');
175                         break;
176                     case 'n':
177                         array.append('\n');
178                         break;
179                     case 'r':
180                         array.append('\r');
181                         break;
182                     case 't':
183                         array.append('\t');
184                         break;
185                     case 'v':
186                         array.append('\v');
187                         break;
188                     case '\\':
189                     case '?':
190                     case '\'':
191                     case '"':
192                         array.append(c.toLatin1());
193                         break;
194
195                     case 'x':
196                         if (start + 2 <= len)
197                             parseError();
198                         array.append(char(line.mid(start + 1, 2).toInt(0, 16)));
199                         break;
200                         
201                     default:
202                         array.append(c.toLatin1());
203                         fprintf(stderr, PROGRAMNAME ": warning: invalid escape sequence '\\%c' found in input",
204                                 c.toLatin1());
205                     }
206                 } else {
207                     // octal
208                     QRegExp octal("([0-7]+)");
209                     if (octal.indexIn(QLatin1String(line), start) == -1)
210                         parseError();
211                     array.append(char(octal.cap(1).toInt(0, 8)));
212                 }
213             } else {
214                 array.append(line.at(start));
215             }
216     }
217
218     parseError();
219 }                    
220
221 void MocParser::parse(const char *fname, QIODevice *io, int lineNumber)
222 {
223     filename = fname;
224     input = io;
225     line = lineNumber;
226
227     while (!input->atEnd()) {
228         QByteArray line = readLine();
229         if (line.startsWith("static const uint qt_meta_data_")) {
230             // start of new class data
231             uint *data;
232             loadIntData(data);
233
234             // find the start of the string data
235             do {
236                 line = readLine();
237                 if (input->atEnd())
238                     parseError();
239             } while (!line.startsWith("static const char qt_meta_stringdata_"));
240
241             char *stringdata;
242             loadStringData(stringdata);
243
244             QMetaObject mo;
245             mo.d.superdata = &QObject::staticMetaObject;
246             mo.d.stringdata = stringdata;
247             mo.d.data = data;
248             mo.d.extradata = 0;
249             objects.append(mo);
250         }
251     }
252
253     fname = 0;
254     input = 0;
255 }
256
257 MocParser::~MocParser()
258 {
259     foreach (QMetaObject mo, objects) {
260         delete const_cast<char *>(mo.d.stringdata);
261         delete const_cast<uint *>(mo.d.data);
262     }
263 }
264
265 static void showHelp()
266 {
267     printf("%s", help);
268     exit(0);
269 }
270
271 static void showVersion()
272 {
273     printf("%s version %s\n", PROGRAMNAME, PROGRAMVERSION);
274     printf("D-Bus QObject-to-XML converter\n");
275     exit(0);
276 }
277
278 static void parseCmdLine(int argc, char **argv)
279 {
280     int c;
281     opterr = true;
282     while ((c = getopt(argc, argv, cmdlineOptions)) != -1)
283         switch (c)
284         {
285         case 'p':
286             flags |= QDBusConnection::ExportProperties;
287             break;
288
289         case 's':
290             flags |= QDBusConnection::ExportSignals;
291             break;
292
293         case 'm':
294             flags |= QDBusConnection::ExportSlots;
295             break;
296
297         case 'a':
298             flags |= QDBusConnection::ExportContents;
299             break;
300
301         case 'P':
302             flags |= QDBusConnection::ExportAllProperties;
303             break;
304
305         case 'S':
306             flags |= QDBusConnection::ExportAllSignals;
307             break;
308
309         case 'M':
310             flags |= QDBusConnection::ExportAllSlots;
311             break;
312
313         case 'A':
314             flags |= QDBusConnection::ExportAllContents;
315             break;
316
317         case 'o':
318             outputFile = optarg;
319             break;
320
321         case 'h':
322             showHelp();
323             break;
324
325         case 'V':
326             showVersion();
327             break;
328
329         case '?':
330             exit(1);
331         default:
332             abort();
333         }
334
335     if (flags == 0)
336         flags = QDBusConnection::ExportAllContents;
337 }
338
339 int main(int argc, char **argv)
340 {
341     MocParser parser;
342     parseCmdLine(argc, argv);
343
344     for (int i = optind; i < argc; ++i) {
345         FILE *in = fopen(argv[i], "r");
346         if (in == 0) {
347             fprintf(stderr, PROGRAMNAME ": could not open '%s': %s\n",
348                     argv[i], strerror(errno));
349             return 1;
350         }
351
352         QFile f;
353         f.open(in, QIODevice::ReadOnly);
354         f.readLine();
355
356         QByteArray line = f.readLine();
357         if (line.contains("Meta object code from reading C++ file"))
358             // this is a moc-generated file
359             parser.parse(argv[i], &f, 3);
360         else {
361             // run moc on this file
362             QProcess proc;
363             proc.start("moc", QStringList() << QFile::encodeName(argv[i]));
364             
365             if (!proc.waitForStarted()) {
366                 fprintf(stderr, PROGRAMNAME ": could not execute moc! Aborting.\n");
367                 return 1;
368             }
369
370             proc.closeWriteChannel();
371
372             if (!proc.waitForFinished() || proc.exitStatus() != QProcess::NormalExit ||
373                 proc.exitCode() != 0) {
374                 // output the moc errors:
375                 fprintf(stderr, "%s", proc.readAllStandardError().constData());
376                 fprintf(stderr, PROGRAMNAME ": exit code %d from moc. Aborting\n", proc.exitCode());
377                 return 1;
378             }
379             fprintf(stderr, "%s", proc.readAllStandardError().constData());
380
381             parser.parse(argv[i], &proc, 1);
382         }
383
384         f.close();
385         fclose(in);
386     }
387
388     FILE *output = stdout;
389     if (outputFile != 0) {
390         output = fopen(outputFile, "w");
391         if (output == 0) {
392             fprintf(stderr, PROGRAMNAME ": could not open output file '%s': %s",
393                     outputFile, strerror(errno));
394             return 1;
395         }
396     }
397
398     fprintf(output, "%s<node>\n", docTypeHeader);
399     foreach (QMetaObject mo, parser.objects) {
400         QString xml = qDBusGenerateMetaObjectXml(QString(), &mo, &QObject::staticMetaObject,
401                                                  flags);
402         fprintf(output, "%s", qPrintable(xml));
403     }
404     fprintf(output, "</node>\n");
405
406     if (output != stdout)
407         fclose(output);
408 }
409