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