1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "proparser.h"
43
44 #include <qdir.h>
45 #include <qfile.h>
46 #include <qfileinfo.h>
47 #include <qregexp.h>
48 #include <qstringlist.h>
49 #include <qtextstream.h>
50
51 #ifdef Q_OS_UNIX
52 #include <unistd.h>
53 #endif
54
55 #ifdef Q_OS_WIN32
56 #define QT_POPEN _popen
57 #else
58 #define QT_POPEN popen
59 #endif
60
loadFile(const QString & fileName)61 QString loadFile( const QString &fileName )
62 {
63 QFile file( fileName );
64 if ( !file.open(QIODevice::ReadOnly) ) {
65 fprintf( stderr, "error: Cannot load '%s': %s\n",
66 file.fileName().toLatin1().constData(),
67 file.errorString().toLatin1().constData() );
68 return QString();
69 }
70
71 QTextStream in( &file );
72 return in.readAll();
73 }
74
proFileTagMap(const QString & text)75 QMap<QString, QString> proFileTagMap( const QString& text )
76 {
77 QString t = text;
78
79 QMap<QString, QString> tagMap;
80 bool stillProcess = true; // If include() has a $$tag then we need to reprocess
81
82 while (stillProcess) {
83 /*
84 Strip any commments before we try to include. We
85 still need to do it after we include to make sure the
86 included file does not have comments
87 */
88 t.replace( QRegExp(QString("#[^\n]*\n")), QString(" ") );
89
90 /*
91 Process include() commands.
92 $$PWD is a special case so we have to change it while
93 we know where the included file is.
94 */
95 QRegExp callToInclude("include\\s*\\(\\s*([^()\\s]+)\\s*\\)");
96 int i = 0;
97 while ( (i = callToInclude.indexIn(t, i)) != -1 ) {
98 bool doneWithVar = false;
99 QString fileName = callToInclude.cap(1);
100 QString after = fileName.replace("$$PWD", QDir::currentPath());
101 if (!tagMap.isEmpty() && after.contains("$$")) {
102 QRegExp var( "\\$\\$[({]?([a-zA-Z0-9_]+)[)}]?" );
103 int ii = 0;
104 while ((ii = after.indexOf(var, ii)) != -1) {
105 if (tagMap.contains(var.cap(1))) {
106 after.replace(ii, var.cap(0).length(), tagMap[var.cap(1)]);
107 } else { // Couldn't find it
108 doneWithVar = true;
109 break;
110 }
111 }
112
113 }
114 if (doneWithVar || !after.contains("$$")) {
115 after = loadFile(after);
116 QFileInfo fi(callToInclude.cap(1));
117 after.replace("$$PWD", fi.path());
118 t.replace( i, callToInclude.matchedLength(), after );
119 }
120 i += after.length();
121 }
122
123 /*
124 Strip comments, merge lines ending with backslash, add
125 spaces around '=' and '+=', replace '\n' with ';', and
126 simplify white spaces.
127 */
128 t.replace( QRegExp(QString("#[^\n]*\n")), QString(" ") );
129 t.replace( QRegExp(QString("\\\\[^\n\\S]*\n")), QString(" ") );
130 t.replace( "=", QString(" = ") );
131 t.replace( "+ =", QString(" += ") );
132 t.replace( "\n", QString(";") );
133 t.replace( "\r", QString("") ); // remove carriage return
134 t = t.simplified();
135
136 /*
137 Populate tagMap with 'key = value' entries.
138 */
139 QStringList lines = t.split(';', QString::SkipEmptyParts);
140 QStringList::Iterator line;
141 for ( line = lines.begin(); line != lines.end(); ++line ) {
142 QStringList toks = (*line).split(' ', QString::SkipEmptyParts);
143
144 if ( toks.count() >= 3 &&
145 (toks[1] == QString("=") || toks[1] == QString("+=") ||
146 toks[1] == QString("*=")) ) {
147 QString tag = toks.first();
148 int k = tag.lastIndexOf( QChar(':') ); // as in 'unix:'
149 if ( k != -1 )
150 tag = tag.mid( k + 1 );
151 toks.erase( toks.begin() );
152
153 QString action = toks.first();
154 toks.erase( toks.begin() );
155
156 if ( tagMap.contains(tag) ) {
157 if ( action == QString("=") )
158 tagMap.insert( tag, toks.join(" ") );
159 else
160 tagMap[tag] += QChar( ' ' ) + toks.join( " " );
161 } else {
162 tagMap[tag] = toks.join( " " );
163 }
164 }
165 }
166 /*
167 Expand $$variables within the 'value' part of a 'key = value'
168 pair.
169 */
170 QRegExp var( "\\$\\$[({]?([a-zA-Z0-9_]+)[)}]?" );
171 QMap<QString, QString>::Iterator it;
172 for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
173 int i = 0;
174 while ( (i = var.indexIn((*it), i)) != -1 ) {
175 int len = var.matchedLength();
176 QString invocation = var.cap(1);
177 QString after;
178
179 if ( invocation == "system" ) {
180 // skip system(); it will be handled in the next pass
181 ++i;
182 } else {
183 if ( tagMap.contains(invocation) )
184 after = tagMap[invocation];
185 else if (invocation.toLower() == "pwd")
186 after = QDir::currentPath();
187 (*it).replace( i, len, after );
188 i += after.length();
189 }
190 }
191 }
192
193 /*
194 Execute system() calls.
195 */
196 QRegExp callToSystem( "\\$\\$system\\s*\\(([^()]*)\\)" );
197 for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
198 int i = 0;
199 while ( (i = callToSystem.indexIn((*it), i)) != -1 ) {
200 /*
201 This code is stolen from qmake's project.cpp file.
202 Ideally we would use the same parser, so we wouldn't
203 have this code duplication.
204 */
205 QString after;
206 char buff[256];
207 FILE *proc = QT_POPEN( callToSystem.cap(1).toLatin1().constData(), "r" );
208 while ( proc && !feof(proc) ) {
209 int read_in = int(fread( buff, 1, 255, proc ));
210 if ( !read_in )
211 break;
212 for ( int i = 0; i < read_in; i++ ) {
213 if ( buff[i] == '\n' || buff[i] == '\t' )
214 buff[i] = ' ';
215 }
216 buff[read_in] = '\0';
217 after += buff;
218 }
219 (*it).replace( i, callToSystem.matchedLength(), after );
220 i += after.length();
221 }
222 }
223 stillProcess = callToInclude.indexIn(t) != -1;
224 }
225 return tagMap;
226 }
227