Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtenvironmentvariables.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <qplatformdefs.h>
9#include <QtCore/qbytearray.h>
10#include <QtCore/qmutex.h>
11#include <QtCore/qstring.h>
12#include <QtCore/qvarlengtharray.h>
13
14#include <QtCore/private/qlocale_p.h>
15#include <QtCore/private/qlocking_p.h>
16
18
19// In the C runtime on all platforms access to the environment is not thread-safe. We
20// add thread-safety for the Qt wrappers.
21Q_CONSTINIT static QBasicMutex environmentMutex;
22
23/*!
24 \headerfile <QtEnvironmentVariables>
25 \inmodule QtCore
26 \ingroup funclists
27 \brief Helper functions for working with environment variables.
28*/
29
30/*!
31 \relates <QtEnvironmentVariables>
32 \threadsafe
33
34 Returns the value of the environment variable with name \a varName as a
35 QByteArray. If no variable by that name is found in the environment, this
36 function returns a default-constructed QByteArray.
37
38 The Qt environment manipulation functions are thread-safe, but this
39 requires that the C library equivalent functions like getenv and putenv are
40 not directly called.
41
42 To convert the data to a QString use QString::fromLocal8Bit().
43
44 \note on desktop Windows, qgetenv() may produce data loss if the
45 original string contains Unicode characters not representable in the
46 ANSI encoding. Use qEnvironmentVariable() instead.
47 On Unix systems, this function is lossless.
48
49 \sa qputenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet(),
50 qEnvironmentVariableIsEmpty(), qEnvironmentVariableIntegerValue()
51*/
52QByteArray qgetenv(const char *varName)
53{
54 const auto locker = qt_scoped_lock(environmentMutex);
55 return QByteArray(::getenv(varName));
56}
57
58/*!
59 \fn QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
60 \fn QString qEnvironmentVariable(const char *varName)
61
62 \relates <QtEnvironmentVariables>
63 \since 5.10
64
65 These functions return the value of the environment variable, \a varName, as a
66 QString. If no variable \a varName is found in the environment and \a defaultValue
67 is provided, \a defaultValue is returned. Otherwise QString() is returned.
68
69 The Qt environment manipulation functions are thread-safe, but this
70 requires that the C library equivalent functions like getenv and putenv are
71 not directly called.
72
73 The following table describes how to choose between qgetenv() and
74 qEnvironmentVariable():
75 \table
76 \header \li Condition \li Recommendation
77 \row
78 \li Variable contains file paths or user text
79 \li qEnvironmentVariable()
80 \row
81 \li Windows-specific code
82 \li qEnvironmentVariable()
83 \row
84 \li Unix-specific code, destination variable is not QString and/or is
85 used to interface with non-Qt APIs
86 \li qgetenv()
87 \row
88 \li Destination variable is a QString
89 \li qEnvironmentVariable()
90 \row
91 \li Destination variable is a QByteArray or std::string
92 \li qgetenv()
93 \endtable
94
95 \note on Unix systems, this function may produce data loss if the original
96 string contains arbitrary binary data that cannot be decoded by the locale
97 codec. Use qgetenv() instead for that case. On Windows, this function is
98 lossless.
99
100 \note the variable name \a varName must contain only US-ASCII characters.
101
102 \sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty(),
103 qEnvironmentVariableIntegerValue()
104*/
105QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
106{
107#if defined(Q_OS_WIN)
108 QVarLengthArray<wchar_t, 32> wname(qsizetype(strlen(varName)) + 1);
109 for (qsizetype i = 0; i < wname.size(); ++i) // wname.size() is correct: will copy terminating null
110 wname[i] = uchar(varName[i]);
111 size_t requiredSize = 0;
112 auto locker = qt_unique_lock(environmentMutex);
113 _wgetenv_s(&requiredSize, 0, 0, wname.data());
114 if (requiredSize == 0)
115 return defaultValue;
116 QString buffer(qsizetype(requiredSize), Qt::Uninitialized);
117 _wgetenv_s(&requiredSize, reinterpret_cast<wchar_t *>(buffer.data()), requiredSize,
118 wname.data());
119 locker.unlock();
120 // requiredSize includes the terminating null, which we don't want.
121 Q_ASSERT(buffer.endsWith(QChar(u'\0')));
122 buffer.chop(1);
123 return buffer;
124#else
125 const auto locker = qt_scoped_lock(environmentMutex);
126 const char *value = ::getenv(varName);
127 if (!value)
128 return defaultValue;
129// duplicated in qfile.h (QFile::decodeName)
130#if defined(Q_OS_DARWIN)
131 return QString::fromUtf8(value).normalized(QString::NormalizationForm_C);
132#else // other Unix
133 return QString::fromLocal8Bit(value);
134#endif
135#endif
136}
137
138QString qEnvironmentVariable(const char *varName)
139{
140 return qEnvironmentVariable(varName, QString());
141}
142
143/*!
144 \relates <QtEnvironmentVariables>
145 \since 5.1
146
147 Returns whether the environment variable \a varName is empty.
148
149 Equivalent to
150 \snippet code/src_corelib_global_qglobal.cpp is-empty
151 except that it's potentially much faster, and can't throw exceptions.
152
153 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
154*/
155bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
156{
157 const auto locker = qt_scoped_lock(environmentMutex);
158 const char * const value = ::getenv(varName);
159 return !value || !*value;
160}
161
162QT_WARNING_PUSH
163QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // older GCC don't like libstdc++'s std::optional
164/*!
165 \relates <QtEnvironmentVariables>
166 \since 5.5
167
168 Returns the numerical value of the environment variable \a varName.
169 If \a ok is not null, sets \c{*ok} to \c true or \c false depending
170 on the success of the conversion.
171
172 Equivalent to
173 \snippet code/src_corelib_global_qglobal.cpp to-int
174 except that it's much faster, and can't throw exceptions.
175
176 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
177*/
178int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept
179{
180 std::optional<qint64> value = qEnvironmentVariableIntegerValue(varName);
181 if (value && *value != int(*value))
182 value = std::nullopt;
183 if (ok)
184 *ok = bool(value);
185 return int(value.value_or(0));
186}
187QT_WARNING_POP
188
189/*!
190 \relates <QtEnvironmentVariables>
191 \since 6.10
192
193 Returns the numerical value of the environment variable \a varName. If the
194 variable is not set or could not be parsed as an integer, it returns
195 \c{std::nullopt}.
196
197 Similar to
198 \snippet code/src_corelib_global_qglobal.cpp to-int
199 except that it's much faster, and can't throw exceptions.
200
201 If a value of zero is semantically the same as an empty or unset variable,
202 applications can use
203 \snippet code/src_corelib_global_qglobal.cpp int-value_or
204 Do note in this case that failures to parse a value will also produce a
205 zero.
206
207 But if a value of zero can be used to disable some functionality,
208 applications can compare the returned \c{std::optional} to zero, which will
209 only be true if the variable was set and contained a number that parsed as
210 zero, as in:
211 \snippet code/src_corelib_global_qglobal.cpp int-eq0
212
213 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
214*/
215std::optional<qint64> qEnvironmentVariableIntegerValue(const char *varName) noexcept
216{
217 const auto locker = qt_scoped_lock(environmentMutex);
218 const char * const buffer = ::getenv(varName);
219 if (!buffer)
220 return std::nullopt;
221 auto r = QLocaleData::bytearrayToLongLong(buffer, 0);
222 if (!r.ok())
223 return std::nullopt;
224 return r.result;
225}
226
227/*!
228 \relates <QtEnvironmentVariables>
229 \since 5.1
230
231 Returns whether the environment variable \a varName is set.
232
233 Equivalent to
234 \snippet code/src_corelib_global_qglobal.cpp is-null
235 except that it's potentially much faster, and can't throw exceptions.
236
237 \sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty(),
238 qEnvironmentVariableIntegerValue()
239*/
240bool qEnvironmentVariableIsSet(const char *varName) noexcept
241{
242 const auto locker = qt_scoped_lock(environmentMutex);
243 return ::getenv(varName) != nullptr;
244}
245
246/*!
247 \fn bool qputenv(const char *varName, QByteArrayView value)
248 \relates <QtEnvironmentVariables>
249
250 This function sets the \a value of the environment variable named
251 \a varName. It will create the variable if it does not exist. It
252 returns 0 if the variable could not be set.
253
254 Calling qputenv with an empty value removes the environment variable on
255 Windows, and makes it set (but empty) on Unix. Prefer using qunsetenv()
256 for fully portable behavior.
257
258 \note qputenv() was introduced because putenv() from the standard
259 C library was deprecated in VC2005 (and later versions). qputenv()
260 uses the replacement function in VC, and calls the standard C
261 library's implementation on all other platforms.
262
263 \note In Qt versions prior to 6.5, the \a value argument was QByteArray,
264 not QByteArrayView.
265
266 \sa qgetenv(), qEnvironmentVariable()
267*/
268bool qputenv(const char *varName, QByteArrayView raw)
269{
270 auto protect = [](const char *str) { return str ? str : ""; };
271
272 std::string value{protect(raw.data()), size_t(raw.size())}; // NUL-terminates w/SSO
273
274#if defined(Q_CC_MSVC)
275 const auto locker = qt_scoped_lock(environmentMutex);
276 return _putenv_s(varName, value.data()) == 0;
277#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU)
278 // POSIX.1-2001 has setenv
279 const auto locker = qt_scoped_lock(environmentMutex);
280 return setenv(varName, value.data(), true) == 0;
281#else
282 std::string buffer;
283 buffer += protect(varName);
284 buffer += '=';
285 buffer += value;
286 char *envVar = qstrdup(buffer.data());
287 int result = [&] {
288 const auto locker = qt_scoped_lock(environmentMutex);
289 return putenv(envVar);
290 }();
291 if (result != 0) // error. we have to delete the string.
292 delete[] envVar;
293 return result == 0;
294#endif
295}
296
297/*!
298 \relates <QtEnvironmentVariables>
299
300 This function deletes the variable \a varName from the environment.
301
302 Returns \c true on success.
303
304 \since 5.1
305
306 \sa qputenv(), qgetenv(), qEnvironmentVariable()
307*/
308bool qunsetenv(const char *varName)
309{
310#if defined(Q_CC_MSVC)
311 const auto locker = qt_scoped_lock(environmentMutex);
312 return _putenv_s(varName, "") == 0;
313#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_BSD4) || defined(Q_OS_HAIKU)
314 // POSIX.1-2001, BSD and Haiku have unsetenv
315 const auto locker = qt_scoped_lock(environmentMutex);
316 return unsetenv(varName) == 0;
317#elif defined(Q_CC_MINGW)
318 // On mingw, putenv("var=") removes "var" from the environment
319 QByteArray buffer(varName);
320 buffer += '=';
321 const auto locker = qt_scoped_lock(environmentMutex);
322 return putenv(buffer.constData()) == 0;
323#else
324 // Fallback to putenv("var=") which will insert an empty var into the
325 // environment and leak it
326 QByteArray buffer(varName);
327 buffer += '=';
328 char *envVar = qstrdup(buffer.constData());
329 const auto locker = qt_scoped_lock(environmentMutex);
330 return putenv(envVar) == 0;
331#endif
332}
333
334/* Various time-related APIs that need to consult system settings also need
335 protection with the same lock as the environment, since those system settings
336 include part of the environment (principally TZ).
337
338 First, tzset(), which POSIX explicitly says accesses the environment.
339*/
340void qTzSet()
341{
342 const auto locker = qt_scoped_lock(environmentMutex);
343#if defined(Q_OS_WIN)
344 _tzset();
345#else
346 tzset();
347#endif // Q_OS_WIN
348}
349
350/* Wrap mktime(), which is specified to behave as if it called tzset(), hence
351 shares its implicit environment-dependence.
352*/
353time_t qMkTime(struct tm *when)
354{
355 const auto locker = qt_scoped_lock(environmentMutex);
356#if defined(Q_OS_WIN)
357 // QTBUG-83881 MS's mktime() seems to need _tzset() called first.
358 _tzset();
359#endif
360 return mktime(when);
361}
362
363/* For localtime(), POSIX mandates that it behave as if it called tzset().
364 For the alternatives to it, we need (if only for compatibility) to do the
365 same explicitly, which should ensure a re-parse of timezone info.
366*/
367bool qLocalTime(time_t utc, struct tm *local)
368{
369 const auto locker = qt_scoped_lock(environmentMutex);
370#if defined(Q_OS_WIN)
371 // The doc of localtime_s() says that it corrects for the same things
372 // _tzset() sets the globals for, but QTBUG-109974 reveals a need for an
373 // explicit call, all the same.
374 _tzset();
375 return !localtime_s(local, &utc);
376#elif QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
377 // Use the reentrant version of localtime() where available, as it is
378 // thread-safe and doesn't use a shared static data area.
379 // As localtime_r() is not specified to work as if it called tzset(),
380 // make an explicit call.
381 tzset();
382 if (tm *res = localtime_r(&utc, local)) {
383 Q_ASSERT(res == local);
384 Q_UNUSED(res);
385 return true;
386 }
387 return false;
388#else
389 // POSIX mandates that localtime() behaves as if it called tzset().
390 // Returns shared static data which may be overwritten at any time (albeit
391 // our lock probably keeps it safe). So copy the result promptly:
392 if (tm *res = localtime(&utc)) {
393 *local = *res;
394 return true;
395 }
396 return false;
397#endif
398}
399
400/* Access to the tzname[] global in one thread is UB if any other is calling
401 tzset() or anything that behaves as if it called tzset(). So also lock this
402 access to prevent such collisions.
403
404 Note that, on Windows, the return is a Microsoft-specific full name for the
405 zone, not an abbreviation.
406
407 Parameter dstIndex must be 1 for DST or 0 for standard time.
408 Returns the relevant form of the name of local-time's zone.
409*/
410QString qTzName(int dstIndex)
411{
412 Q_DECL_UNINITIALIZED
413 char name[512];
414 bool ok;
415 size_t size = 0;
416 {
417 const auto locker = qt_scoped_lock(environmentMutex);
418#if defined(_UCRT) // i.e., MSVC and MinGW-UCRT
419 ok = _get_tzname(&size, name, sizeof(name), dstIndex) == 0;
420 size -= 1; // It includes space for '\0' (or !ok => we're going to ignore it).
421 Q_ASSERT(!ok || size < sizeof(name));
422#else
423 const char *const src = tzname[dstIndex];
424 size = src ? strlen(src) : 0;
425 ok = src != nullptr && size < sizeof(name);
426 if (ok)
427 memcpy(name, src, size + 1);
428#endif // _UCRT
429 }
430 return ok ? QString::fromLocal8Bit(name, qsizetype(size)) : QString();
431}
432
433QT_END_NAMESPACE
Combined button and popup list for selecting options.
bool qLocalTime(time_t utc, struct tm *local)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
bool qputenv(const char *varName, QByteArrayView raw)
time_t qMkTime(struct tm *when)
void qTzSet()
QString qTzName(int dstIndex)
QString qEnvironmentVariable(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
Q_CORE_EXPORT bool qunsetenv(const char *varName)