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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/corelib/global/qtenvironmentvariables.cpp