Skip to content

Commit 908d048

Browse files
author
Sona Kurazyan
committed
Extract header qenvironmentvariables.h from qglobal.h
qcontainerfwd.h was relying on the forward declaration of QByteArray in qglobal.h, so add the missing forward declaration there. Additionally, had to move the implementations of qTzSet() and qMkTime() to qenvironmentvariables.cpp along with environmentMutex. Task-number: QTBUG-99313 Change-Id: I233aff305c2fedaf0a48362cc99ff2d6f6c0ee54 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
1 parent 349e74c commit 908d048

File tree

7 files changed

+375
-340
lines changed

7 files changed

+375
-340
lines changed

src/corelib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ qt_internal_add_module(Core
4949
global/qcompilerdetection.h
5050
global/qcontainerinfo.h
5151
global/qendian.cpp global/qendian.h global/qendian_p.h
52+
global/qenvironmentvariables.cpp global/qenvironmentvariables.h
5253
global/qflags.h
5354
global/qfloat16.cpp global/qfloat16.h
5455
global/qforeach.h
Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
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 "qenvironmentvariables.h"
5+
6+
#include <qplatformdefs.h>
7+
#include <QtCore/qbytearray.h>
8+
#include <QtCore/qmutex.h>
9+
#include <QtCore/qstring.h>
10+
#include <QtCore/qvarlengtharray.h>
11+
12+
#include <QtCore/private/qlocking_p.h>
13+
14+
QT_BEGIN_NAMESPACE
15+
16+
// In the C runtime on all platforms access to the environment is not thread-safe. We
17+
// add thread-safety for the Qt wrappers.
18+
Q_CONSTINIT static QBasicMutex environmentMutex;
19+
20+
/*!
21+
\relates <QEnvironmentVariables>
22+
\threadsafe
23+
24+
Returns the value of the environment variable with name \a varName as a
25+
QByteArray. If no variable by that name is found in the environment, this
26+
function returns a default-constructed QByteArray.
27+
28+
The Qt environment manipulation functions are thread-safe, but this
29+
requires that the C library equivalent functions like getenv and putenv are
30+
not directly called.
31+
32+
To convert the data to a QString use QString::fromLocal8Bit().
33+
34+
\note on desktop Windows, qgetenv() may produce data loss if the
35+
original string contains Unicode characters not representable in the
36+
ANSI encoding. Use qEnvironmentVariable() instead.
37+
On Unix systems, this function is lossless.
38+
39+
\sa qputenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet(),
40+
qEnvironmentVariableIsEmpty()
41+
*/
42+
QByteArray qgetenv(const char *varName)
43+
{
44+
const auto locker = qt_scoped_lock(environmentMutex);
45+
#ifdef Q_CC_MSVC
46+
size_t requiredSize = 0;
47+
QByteArray buffer;
48+
getenv_s(&requiredSize, 0, 0, varName);
49+
if (requiredSize == 0)
50+
return buffer;
51+
buffer.resize(int(requiredSize));
52+
getenv_s(&requiredSize, buffer.data(), requiredSize, varName);
53+
// requiredSize includes the terminating null, which we don't want.
54+
Q_ASSERT(buffer.endsWith('\0'));
55+
buffer.chop(1);
56+
return buffer;
57+
#else
58+
return QByteArray(::getenv(varName));
59+
#endif
60+
}
61+
62+
/*!
63+
\fn QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
64+
\fn QString qEnvironmentVariable(const char *varName)
65+
66+
\relates <QEnvironmentVariables>
67+
\since 5.10
68+
69+
These functions return the value of the environment variable, \a varName, as a
70+
QString. If no variable \a varName is found in the environment and \a defaultValue
71+
is provided, \a defaultValue is returned. Otherwise QString() is returned.
72+
73+
The Qt environment manipulation functions are thread-safe, but this
74+
requires that the C library equivalent functions like getenv and putenv are
75+
not directly called.
76+
77+
The following table describes how to choose between qgetenv() and
78+
qEnvironmentVariable():
79+
\table
80+
\header \li Condition \li Recommendation
81+
\row
82+
\li Variable contains file paths or user text
83+
\li qEnvironmentVariable()
84+
\row
85+
\li Windows-specific code
86+
\li qEnvironmentVariable()
87+
\row
88+
\li Unix-specific code, destination variable is not QString and/or is
89+
used to interface with non-Qt APIs
90+
\li qgetenv()
91+
\row
92+
\li Destination variable is a QString
93+
\li qEnvironmentVariable()
94+
\row
95+
\li Destination variable is a QByteArray or std::string
96+
\li qgetenv()
97+
\endtable
98+
99+
\note on Unix systems, this function may produce data loss if the original
100+
string contains arbitrary binary data that cannot be decoded by the locale
101+
codec. Use qgetenv() instead for that case. On Windows, this function is
102+
lossless.
103+
104+
\note the variable name \a varName must contain only US-ASCII characters.
105+
106+
\sa qputenv(), qgetenv(), qEnvironmentVariableIsSet(), qEnvironmentVariableIsEmpty()
107+
*/
108+
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
109+
{
110+
#if defined(Q_OS_WIN)
111+
const auto locker = qt_scoped_lock(environmentMutex);
112+
QVarLengthArray<wchar_t, 32> wname(int(strlen(varName)) + 1);
113+
for (int 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+
QString buffer;
117+
_wgetenv_s(&requiredSize, 0, 0, wname.data());
118+
if (requiredSize == 0)
119+
return defaultValue;
120+
buffer.resize(int(requiredSize));
121+
_wgetenv_s(&requiredSize, reinterpret_cast<wchar_t *>(buffer.data()), requiredSize,
122+
wname.data());
123+
// requiredSize includes the terminating null, which we don't want.
124+
Q_ASSERT(buffer.endsWith(QChar(u'\0')));
125+
buffer.chop(1);
126+
return buffer;
127+
#else
128+
QByteArray value = qgetenv(varName);
129+
if (value.isNull())
130+
return defaultValue;
131+
// duplicated in qfile.h (QFile::decodeName)
132+
#if defined(Q_OS_DARWIN)
133+
return QString::fromUtf8(value).normalized(QString::NormalizationForm_C);
134+
#else // other Unix
135+
return QString::fromLocal8Bit(value);
136+
#endif
137+
#endif
138+
}
139+
140+
QString qEnvironmentVariable(const char *varName)
141+
{
142+
return qEnvironmentVariable(varName, QString());
143+
}
144+
145+
/*!
146+
\relates <QEnvironmentVariables>
147+
\since 5.1
148+
149+
Returns whether the environment variable \a varName is empty.
150+
151+
Equivalent to
152+
\snippet code/src_corelib_global_qglobal.cpp is-empty
153+
except that it's potentially much faster, and can't throw exceptions.
154+
155+
\sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
156+
*/
157+
bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
158+
{
159+
const auto locker = qt_scoped_lock(environmentMutex);
160+
#ifdef Q_CC_MSVC
161+
// we provide a buffer that can only hold the empty string, so
162+
// when the env.var isn't empty, we'll get an ERANGE error (buffer
163+
// too small):
164+
size_t dummy;
165+
char buffer = '\0';
166+
return getenv_s(&dummy, &buffer, 1, varName) != ERANGE;
167+
#else
168+
const char * const value = ::getenv(varName);
169+
return !value || !*value;
170+
#endif
171+
}
172+
173+
/*!
174+
\relates <QEnvironmentVariables>
175+
\since 5.5
176+
177+
Returns the numerical value of the environment variable \a varName.
178+
If \a ok is not null, sets \c{*ok} to \c true or \c false depending
179+
on the success of the conversion.
180+
181+
Equivalent to
182+
\snippet code/src_corelib_global_qglobal.cpp to-int
183+
except that it's much faster, and can't throw exceptions.
184+
185+
\note there's a limit on the length of the value, which is sufficient for
186+
all valid values of int, not counting leading zeroes or spaces. Values that
187+
are too long will either be truncated or this function will set \a ok to \c
188+
false.
189+
190+
\sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsSet()
191+
*/
192+
int qEnvironmentVariableIntValue(const char *varName, bool *ok) noexcept
193+
{
194+
static const int NumBinaryDigitsPerOctalDigit = 3;
195+
static const int MaxDigitsForOctalInt =
196+
(std::numeric_limits<uint>::digits + NumBinaryDigitsPerOctalDigit - 1) / NumBinaryDigitsPerOctalDigit;
197+
198+
const auto locker = qt_scoped_lock(environmentMutex);
199+
size_t size;
200+
#ifdef Q_CC_MSVC
201+
// we provide a buffer that can hold any int value:
202+
char buffer[MaxDigitsForOctalInt + 2]; // +1 for NUL +1 for optional '-'
203+
size_t dummy;
204+
if (getenv_s(&dummy, buffer, sizeof buffer, varName) != 0) {
205+
if (ok)
206+
*ok = false;
207+
return 0;
208+
}
209+
size = strlen(buffer);
210+
#else
211+
const char * const buffer = ::getenv(varName);
212+
if (!buffer || (size = strlen(buffer)) > MaxDigitsForOctalInt + 2) {
213+
if (ok)
214+
*ok = false;
215+
return 0;
216+
}
217+
#endif
218+
return QByteArrayView(buffer, size).toInt(ok, 0);
219+
}
220+
221+
/*!
222+
\relates <QEnvironmentVariables>
223+
\since 5.1
224+
225+
Returns whether the environment variable \a varName is set.
226+
227+
Equivalent to
228+
\snippet code/src_corelib_global_qglobal.cpp is-null
229+
except that it's potentially much faster, and can't throw exceptions.
230+
231+
\sa qgetenv(), qEnvironmentVariable(), qEnvironmentVariableIsEmpty()
232+
*/
233+
bool qEnvironmentVariableIsSet(const char *varName) noexcept
234+
{
235+
const auto locker = qt_scoped_lock(environmentMutex);
236+
#ifdef Q_CC_MSVC
237+
size_t requiredSize = 0;
238+
(void)getenv_s(&requiredSize, 0, 0, varName);
239+
return requiredSize != 0;
240+
#else
241+
return ::getenv(varName) != nullptr;
242+
#endif
243+
}
244+
245+
/*!
246+
\relates <QEnvironmentVariables>
247+
248+
This function sets the \a value of the environment variable named
249+
\a varName. It will create the variable if it does not exist. It
250+
returns 0 if the variable could not be set.
251+
252+
Calling qputenv with an empty value removes the environment variable on
253+
Windows, and makes it set (but empty) on Unix. Prefer using qunsetenv()
254+
for fully portable behavior.
255+
256+
\note qputenv() was introduced because putenv() from the standard
257+
C library was deprecated in VC2005 (and later versions). qputenv()
258+
uses the replacement function in VC, and calls the standard C
259+
library's implementation on all other platforms.
260+
261+
\sa qgetenv(), qEnvironmentVariable()
262+
*/
263+
bool qputenv(const char *varName, const QByteArray &value)
264+
{
265+
const auto locker = qt_scoped_lock(environmentMutex);
266+
#if defined(Q_CC_MSVC)
267+
return _putenv_s(varName, value.constData()) == 0;
268+
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_HAIKU)
269+
// POSIX.1-2001 has setenv
270+
return setenv(varName, value.constData(), true) == 0;
271+
#else
272+
QByteArray buffer(varName);
273+
buffer += '=';
274+
buffer += value;
275+
char *envVar = qstrdup(buffer.constData());
276+
int result = putenv(envVar);
277+
if (result != 0) // error. we have to delete the string.
278+
delete[] envVar;
279+
return result == 0;
280+
#endif
281+
}
282+
283+
/*!
284+
\relates <QEnvironmentVariables>
285+
286+
This function deletes the variable \a varName from the environment.
287+
288+
Returns \c true on success.
289+
290+
\since 5.1
291+
292+
\sa qputenv(), qgetenv(), qEnvironmentVariable()
293+
*/
294+
bool qunsetenv(const char *varName)
295+
{
296+
const auto locker = qt_scoped_lock(environmentMutex);
297+
#if defined(Q_CC_MSVC)
298+
return _putenv_s(varName, "") == 0;
299+
#elif (defined(_POSIX_VERSION) && (_POSIX_VERSION-0) >= 200112L) || defined(Q_OS_BSD4) || defined(Q_OS_HAIKU)
300+
// POSIX.1-2001, BSD and Haiku have unsetenv
301+
return unsetenv(varName) == 0;
302+
#elif defined(Q_CC_MINGW)
303+
// On mingw, putenv("var=") removes "var" from the environment
304+
QByteArray buffer(varName);
305+
buffer += '=';
306+
return putenv(buffer.constData()) == 0;
307+
#else
308+
// Fallback to putenv("var=") which will insert an empty var into the
309+
// environment and leak it
310+
QByteArray buffer(varName);
311+
buffer += '=';
312+
char *envVar = qstrdup(buffer.constData());
313+
return putenv(envVar) == 0;
314+
#endif
315+
}
316+
317+
/*
318+
Wraps tzset(), which accesses the environment, so should only be called while
319+
we hold the lock on the environment mutex.
320+
*/
321+
void qTzSet()
322+
{
323+
const auto locker = qt_scoped_lock(environmentMutex);
324+
#if defined(Q_OS_WIN)
325+
_tzset();
326+
#else
327+
tzset();
328+
#endif // Q_OS_WIN
329+
}
330+
331+
/*
332+
Wrap mktime(), which is specified to behave as if it called tzset(), hence
333+
shares its implicit environment-dependence.
334+
*/
335+
time_t qMkTime(struct tm *when)
336+
{
337+
const auto locker = qt_scoped_lock(environmentMutex);
338+
return mktime(when);
339+
}
340+
341+
QT_END_NAMESPACE
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
#ifndef QENVIRONMENTVARIABLES_H
5+
#define QENVIRONMENTVARIABLES_H
6+
7+
#include <QtCore/qglobal.h>
8+
9+
#if 0
10+
#pragma qt_class(QEnvironmentVariables)
11+
#pragma qt_sync_stop_processing
12+
#endif
13+
14+
QT_BEGIN_NAMESPACE
15+
16+
class QByteArray;
17+
Q_CORE_EXPORT QByteArray qgetenv(const char *varName);
18+
// need it as two functions because QString is only forward-declared here
19+
Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName);
20+
Q_CORE_EXPORT QString qEnvironmentVariable(const char *varName, const QString &defaultValue);
21+
Q_CORE_EXPORT bool qputenv(const char *varName, const QByteArray &value);
22+
Q_CORE_EXPORT bool qunsetenv(const char *varName);
23+
24+
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept;
25+
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept;
26+
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept;
27+
28+
QT_END_NAMESPACE
29+
30+
#endif /* QENVIRONMENTVARIABLES_H */

0 commit comments

Comments
 (0)