OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / environment.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "environment.h"
35
36 #include <QtCore/QProcess>
37 #include <QtCore/QDir>
38 #include <QtCore/QString>
39
40 using namespace Utils;
41
42 QList<EnvironmentItem> EnvironmentItem::fromStringList(QStringList list)
43 {
44     QList<EnvironmentItem> result;
45     foreach (const QString &string, list) {
46         int pos = string.indexOf(QLatin1Char('='));
47         if (pos == -1) {
48             EnvironmentItem item(string, QString());
49             item.unset = true;
50             result.append(item);
51         } else {
52             EnvironmentItem item(string.left(pos), string.mid(pos+1));
53             result.append(item);
54         }
55     }
56     return result;
57 }
58
59 QStringList EnvironmentItem::toStringList(QList<EnvironmentItem> list)
60 {
61     QStringList result;
62     foreach (const EnvironmentItem &item, list) {
63         if (item.unset)
64             result << QString(item.name);
65         else
66             result << QString(item.name + '=' + item.value);
67     }
68     return result;
69 }
70
71 Environment::Environment()
72 {
73 }
74
75 Environment::Environment(QStringList env)
76 {
77     foreach (const QString &s, env) {
78         int i = s.indexOf("=");
79         if (i >= 0) {
80 #ifdef Q_OS_WIN
81             m_values.insert(s.left(i).toUpper(), s.mid(i+1));
82 #else
83             m_values.insert(s.left(i), s.mid(i+1));
84 #endif
85         }
86     }
87 }
88
89 QStringList Environment::toStringList() const
90 {
91     QStringList result;
92     const QMap<QString, QString>::const_iterator end = m_values.constEnd();
93     for (QMap<QString, QString>::const_iterator it = m_values.constBegin(); it != end; ++it) {
94         QString entry = it.key();
95         entry += QLatin1Char('=');
96         entry += it.value();
97         result.push_back(entry);
98     }
99     return result;
100 }
101
102 void Environment::set(const QString &key, const QString &value)
103 {
104 #ifdef Q_OS_WIN
105     QString _key = key.toUpper();
106 #else
107     const QString &_key = key;
108 #endif
109     m_values.insert(_key, value);
110 }
111
112 void Environment::unset(const QString &key)
113 {
114 #ifdef Q_OS_WIN
115     QString _key = key.toUpper();
116 #else
117     const QString &_key = key;
118 #endif
119     m_values.remove(_key);
120 }
121
122 void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep)
123 {
124 #ifdef Q_OS_WIN
125     QString _key = key.toUpper();
126 #else
127     const QString &_key = key;
128 #endif
129     QMap<QString, QString>::iterator it = m_values.find(key);
130     if (it == m_values.end()) {
131         m_values.insert(_key, value);
132     } else {
133         // Append unless it is already there
134         const QString toAppend = sep + value;
135         if (!it.value().endsWith(toAppend))
136             it.value().append(toAppend);
137     }
138 }
139
140 void Environment::prependOrSet(const QString&key, const QString &value, const QString &sep)
141 {
142 #ifdef Q_OS_WIN
143     QString _key = key.toUpper();
144 #else
145     const QString &_key = key;
146 #endif
147     QMap<QString, QString>::iterator it = m_values.find(key);
148     if (it == m_values.end()) {
149         m_values.insert(_key, value);
150     } else {
151         // Prepend unless it is already there
152         const QString toPrepend = value + sep;
153         if (!it.value().startsWith(toPrepend))
154             it.value().prepend(toPrepend);
155     }
156 }
157
158 void Environment::appendOrSetPath(const QString &value)
159 {
160 #ifdef Q_OS_WIN
161     const QChar sep = QLatin1Char(';');
162 #else
163     const QChar sep = QLatin1Char(':');
164 #endif
165     appendOrSet(QLatin1String("PATH"), QDir::toNativeSeparators(value), QString(sep));
166 }
167
168 void Environment::prependOrSetPath(const QString &value)
169 {
170 #ifdef Q_OS_WIN
171     const QChar sep = QLatin1Char(';');
172 #else
173     const QChar sep = QLatin1Char(':');
174 #endif
175     prependOrSet(QLatin1String("PATH"), QDir::toNativeSeparators(value), QString(sep));
176 }
177
178 Environment Environment::systemEnvironment()
179 {
180     return Environment(QProcess::systemEnvironment());
181 }
182
183 void Environment::clear()
184 {
185     m_values.clear();
186 }
187
188 QString Environment::searchInPath(const QString &executable,
189                                   const QStringList &additionalDirs) const
190 {
191     QStringList execs;
192     execs << executable;
193 #ifdef Q_OS_WIN
194     // Check all the executable extensions on windows:
195     QStringList extensions = value(QLatin1String("PATHEXT")).split(QLatin1Char(';'));
196
197     // .exe.bat is legal (and run when starting new.exe), so always go through the complete list once:
198     foreach (const QString &ext, extensions)
199         execs << executable + ext.toLower();
200 #endif
201     return searchInPath(execs, additionalDirs);
202 }
203
204 QString Environment::searchInPath(const QStringList &executables,
205                                   const QStringList &additionalDirs) const
206 {
207     foreach (const QString &executable, executables) {
208         QString exec = expandVariables(executable);
209
210         if (exec.isEmpty())
211             continue;
212
213         QFileInfo baseFi(exec);
214         if (baseFi.isAbsolute() && baseFi.exists())
215             return QDir::toNativeSeparators(exec);
216
217         // Check in directories:
218         foreach (const QString &dir, additionalDirs) {
219             if (dir.isEmpty())
220                 continue;
221             QFileInfo fi(dir + QLatin1Char('/') + exec);
222             if (fi.isFile() && fi.isExecutable())
223                 return fi.absoluteFilePath();
224         }
225
226         // Check in path:
227         const QChar slash = QLatin1Char('/');
228         if (exec.indexOf(slash) != -1)
229             continue;
230         foreach (const QString &p, path()) {
231             QString fp = p;
232             fp += slash;
233             fp += exec;
234             const QFileInfo fi(fp);
235             if (fi.exists())
236                 return fi.absoluteFilePath();
237         }
238     }
239     return QString();
240 }
241
242 QStringList Environment::path() const
243 {
244 #ifdef Q_OS_WIN
245     const QChar sep = QLatin1Char(';');
246 #else
247     const QChar sep = QLatin1Char(':');
248 #endif
249     return m_values.value(QLatin1String("PATH")).split(sep, QString::SkipEmptyParts);
250 }
251
252 QString Environment::value(const QString &key) const
253 {
254     return m_values.value(key);
255 }
256
257 QString Environment::key(Environment::const_iterator it) const
258 {
259     return it.key();
260 }
261
262 QString Environment::value(Environment::const_iterator it) const
263 {
264     return it.value();
265 }
266
267 Environment::const_iterator Environment::constBegin() const
268 {
269     return m_values.constBegin();
270 }
271
272 Environment::const_iterator Environment::constEnd() const
273 {
274     return m_values.constEnd();
275 }
276
277 Environment::const_iterator Environment::constFind(const QString &name) const
278 {
279     QMap<QString, QString>::const_iterator it = m_values.constFind(name);
280     if (it == m_values.constEnd())
281         return constEnd();
282     else
283         return it;
284 }
285
286 int Environment::size() const
287 {
288     return m_values.size();
289 }
290
291 void Environment::modify(const QList<EnvironmentItem> & list)
292 {
293     Environment resultEnvironment = *this;
294     foreach (const EnvironmentItem &item, list) {
295         if (item.unset) {
296             resultEnvironment.unset(item.name);
297         } else {
298             // TODO use variable expansion
299             QString value = item.value;
300             for (int i=0; i < value.size(); ++i) {
301                 if (value.at(i) == QLatin1Char('$')) {
302                     if ((i + 1) < value.size()) {
303                         const QChar &c = value.at(i+1);
304                         int end = -1;
305                         if (c == '(')
306                             end = value.indexOf(')', i);
307                         else if (c == '{')
308                             end = value.indexOf('}', i);
309                         if (end != -1) {
310                             const QString &name = value.mid(i+2, end-i-2);
311                             Environment::const_iterator it = constFind(name);
312                             if (it != constEnd())
313                                 value.replace(i, end-i+1, it.value());
314                         }
315                     }
316                 }
317             }
318             resultEnvironment.set(item.name, value);
319         }
320     }
321     *this = resultEnvironment;
322 }
323
324 bool Environment::hasKey(const QString &key)
325 {
326     return m_values.contains(key);
327 }
328
329 bool Environment::operator!=(const Environment &other) const
330 {
331     return !(*this == other);
332 }
333
334 bool Environment::operator==(const Environment &other) const
335 {
336     return m_values == other.m_values;
337 }
338
339 /** Expand environment variables in a string.
340  *
341  * Environment variables are accepted in the following forms:
342  * $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows.
343  * No escapes and quoting are supported.
344  * If a variable is not found, it is not substituted.
345  */
346 QString Environment::expandVariables(const QString &input) const
347 {
348     QString result = input;
349
350 #ifdef Q_OS_WIN
351     for (int vStart = -1, i = 0; i < result.length(); ) {
352         if (result.at(i++) == QLatin1Char('%')) {
353             if (vStart > 0) {
354                 const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1).toUpper());
355                 if (it != m_values.constEnd()) {
356                     result.replace(vStart - 1, i - vStart + 1, *it);
357                     i = vStart - 1 + it->length();
358                     vStart = -1;
359                 } else {
360                     vStart = i;
361                 }
362             } else {
363                 vStart = i;
364             }
365         }
366     }
367 #else
368     enum { BASE, OPTIONALVARIABLEBRACE, VARIABLE, BRACEDVARIABLE } state = BASE;
369     int vStart = -1;
370
371     for (int i = 0; i < result.length();) {
372         QChar c = result.at(i++);
373         if (state == BASE) {
374             if (c == QLatin1Char('$'))
375                 state = OPTIONALVARIABLEBRACE;
376         } else if (state == OPTIONALVARIABLEBRACE) {
377             if (c == QLatin1Char('{')) {
378                 state = BRACEDVARIABLE;
379                 vStart = i;
380             } else if (c.isLetterOrNumber() || c == QLatin1Char('_')) {
381                 state = VARIABLE;
382                 vStart = i - 1;
383             } else {
384                 state = BASE;
385             }
386         } else if (state == BRACEDVARIABLE) {
387             if (c == QLatin1Char('}')) {
388                 const_iterator it = m_values.constFind(result.mid(vStart, i - 1 - vStart));
389                 if (it != constEnd()) {
390                     result.replace(vStart - 2, i - vStart + 2, *it);
391                     i = vStart - 2 + it->length();
392                 }
393                 state = BASE;
394             }
395         } else if (state == VARIABLE) {
396             if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
397                 const_iterator it = m_values.constFind(result.mid(vStart, i - vStart - 1));
398                 if (it != constEnd()) {
399                     result.replace(vStart - 1, i - vStart, *it);
400                     i = vStart - 1 + it->length();
401                 }
402                 state = BASE;
403             }
404         }
405     }
406     if (state == VARIABLE) {
407         const_iterator it = m_values.constFind(result.mid(vStart));
408         if (it != constEnd())
409             result.replace(vStart - 1, result.length() - vStart + 1, *it);
410     }
411 #endif
412     return result;
413 }
414
415 QStringList Environment::expandVariables(const QStringList &variables) const
416 {
417     QStringList results;
418     foreach (const QString & i, variables)
419         results << expandVariables(i);
420     return results;
421 }